前言
接手这个项目已经半年有余,半年的需求迭代也使这个原本的小型项目慢慢变大,逻辑也慢慢更复杂;也是因为之前的整体架构设计扩展性、复用性太差,导致现在维护起来越来越蛋疼;各种活动加版本迭代(产品真是玩出了花)已经需要3个分支同时进行开发;代码的冗余也随之而来,总之,我终于受不了。。。
说干就干:
分析阶段
1,原先的整体架构是FIS3构建+JQ,考虑到有些页面是要内嵌到APP,如果做SPA,客户端也要配合更新,代价太大,而且经过和产品的沟通,我也没有那么多的时间来重构(只能默默抽时间来搞),最后还是选择FIS3+SASS+zepto+modJS+ES6+artTemplate 的技术架构。
2,项目中最乱的就是各种自定义弹窗(大概几十个),风格不一,代码也五花八门,代码也想当耦合;纯粹的业务逻辑堆叠式开发,会导致功能无法重用;
3,统计埋点的解耦,之前的埋点统计逻辑和业务逻辑混合在一起;
4,公共模块的抽出与打包策略
设计阶段
1,和设计妹纸商量统一页面风格,弹窗风格
2,设定目录规范,配置相应的构建配置,搭好整体架子,模块化采用conmmonjs规范
3,构建utils全局公共方法类,拆分模块,细化模块
4,使用promise重新封装全站ajax请求,封装公用异步事件
5,引用弹窗组件,并在此基础上二次封装全站自定义弹窗成一个模块
6,在body上做事件委托,点击的时候进行递归查询父节点是否含有该私有属性,进行相应处理。解耦统计埋点
7,分离移动端和PC端项目
实现阶段
目录结构:
app: 页面程序入口
components: 全站的模块化组件和依赖需要模块化加载的库( 没有做区分 )
lib:需要提前加载的库
src: 全站引用的资源( 图片 | 脚本(包括公共脚本) | 样式 )
tpl:模板存放目录( 不需要发布 )
部分主要配置:
// 引入模块化开发插件,设置为 commonjs 规范。 fis.hook('commonjs',{ baseUrl: '/components', extList: ['.js', '.es'] }); // 启用node-sass 插件 , 解析 .scss 后缀为 fis.match('*.scss', { rExt: '.css', parser: fis.plugin('node-sass', {}), }); // 启用 es6-babel 插件,解析 .es6 后缀为 .js fis.match('**.es', { rExt: '.js', // parser: fis.plugin('es6-babel'), parser: fis.plugin('babel-5.x'), isMod: true }); /*************************主目录规范*****************************/ // ------ 配置src 目录 fis.match("/src/**", { isMod: true, release: '${project.static}/$&' }); fis.match("/lib/**", { isMod: false }); fis.match("/components/{**,/**}", { isMod: true, useSameNameRequire: true, release: '${project.static}/$&' }); fis.match("/components/{**.html,/**.html}", { release: false }); // ------ 配置app 目录 fis.match("/app/**", { isMod: true, release: '${project.static}/$&' }); /************************* 文件处理 *****************************/ // ------ 配置css压缩 md5 启用精灵图 fis.match('**.{css,scss}', { useHash: true, useSprite: true, preprocessor : fis.plugin("autoprefixer",{ "browsers": ["Android >= 2.1", "iOS >= 4", "ie >= 8", "firefox >= 15"], "cascade": true }), optimizer: fis.plugin("clean-css") }); // ------ 配置html压缩 fis.match('*.html', { optimizer: fis.plugin('html-compress') }); // ------ 配置图片md5 fis.match('{**.jpg,**.png,**.gif}', { useHash: true, }); // ------ 配置图片压缩 fis.match('*.png', { optimizer: fis.plugin('png-compressor') }); // ------ 配置js压缩 fis.match('**.{js,es}', { useHash: true, optimizer: fis.plugin("uglify-js", { mangle: { except: 'exports, module, require, define', eval: true }, compress: { drop_console: true, keep_fargs: false } }) }); /*************************文件处理 END *****************************/ // ------ 配置打包 fis.match('::package', { // npm install [-g] fis3-postpackager-loader // 分析 __RESOURCE_MAP__ 结构,来解决资源加载问题 postpackager: fis.plugin('loader', { resourceType: 'commonjs', useInlineMap: true // 资源映射表内嵌 }) }); /************************* 打包策略 *****************************/ fis.match("components/{*,**/*}.{js,es}", { packTo: "pkg/components.js", }); fis.match("app/{*,**/*}.{js,es}", { packTo: "pkg/app.js", }); fis.match("src/js/{*,**/*}.{js,es}", { packTo: "pkg/base.js", }); fis.match("components/{*,**/*}.{css,scss}", { packTo: "pkg/components.css", });
1,利用前端模板引擎和FIS3的资源引入能力,分离模板和页面;把数据和渲染完全分开;
2,函数式编程思想,使流程更加清晰
3,公共脚本注册全局时间,注册全局模板过滤器
4, 统一页面初始化流程,程序结构更加清晰
例如:统一处理页面链接上的参数
replaceHref(param){ const _keys = Util.getUrlKey(); // 检查链接如果没有带渠道号 ,如果本地有 把本地的渠道号加到链接上 // 因为检查UDID的时候已经把链接上的渠道号存入本地, // 所以只需检查本地有无渠道号把本地渠道号加到链接上即可 const _localchannelid = this.getCookie("xxipa_storage_channelid"); if (_localchannelid) { _keys.channelid = _localchannelid; } this.dataType(param)=='Array' && JSON.stringify(_keys) !== "{}" && param.forEach(function(v,i){ _keys.hasOwnProperty(v) && delete _keys[v]; }) // 根据传过来的key重新处理链接上的参数 history.replaceState && history.replaceState(null, "", window.location.href.split('?')[0] + (this.param(_keys) == "" ? "":"?"+this.param(_keys)) ); }
5,公共流程的抽出,以及回调的设计,本次都是采用promise的方案,但是感觉也并不是那么理想,下次准备使用 es7的 async 和 await 重新设计
最后,关于项目重构的理解
首先,前端项目重构的基础是必须要先理解透彻业务逻辑,做到能够把控全局,然后分析代码,设定优化方案;评估重构的影响面及可能的风险;
然后,业务流程拆分,功能模块拆分,各个击破;
最后,当然是要全面的测试了。
目前重构版本已经上线一个月了,再修复了几个隐藏逻辑的bug之后,目前也比较稳定,最近几次活动增删逻辑,也可以游刃有余的应对,不过也感觉还有一些可以优化的空间。
总之,只要项目还需要迭代,项目就还需要重构!谁也不知道产品下一个需求又会有什么样的脑洞。。。