前言
接手这个项目已经半年有余,半年的需求迭代也使这个原本的小型项目慢慢变大,逻辑也慢慢更复杂;也是因为之前的整体架构设计扩展性、复用性太差,导致现在维护起来越来越蛋疼;各种活动加版本迭代(产品真是玩出了花)已经需要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之后,目前也比较稳定,最近几次活动增删逻辑,也可以游刃有余的应对,不过也感觉还有一些可以优化的空间。
总之,只要项目还需要迭代,项目就还需要重构!谁也不知道产品下一个需求又会有什么样的脑洞。。。
