首发: 趣谈前端
作者: 徐小夕
hi, 大家好, 我是徐小夕, 今天和大家分享一下前端项目重构的一些思考和复盘, 同时也是对自己多年项目研发经验的一个总结.
一. 背景介绍
1. 我们为什么要做项目重构
项目重构是每一家稳定发展的互联企业的必经之路, 就像一个产品的诞生, 会经历产品试错和产品迭代 一样, 随着业务或新技术的不断发展, 已有架构已无法满足更多业务扩展的需求, 所以只有通过重构来让产品“进化”, 才能跟上飞速发展的时代浪潮.
这里我结合自己的实际经验总结一下项目重构的几个原因:
1. 技术因素
技术因素主要有如下几个方面:
- 早期技术团队在技术选型上的误判(常发生于MVP类型的产品快速上线导致的技术调研不够充分)
- 新老技术框架的更替(比如从 jquery 迁移到 vue/react)
- 技术团队交接出现的断层(老技术团队的架构设计更不上新技术团队的发展, 出现架构上的“平替”)
- 技术架构升级(比如随着业务发展, 由传统的MPA应用转为基于微前端模式的SPA应用)
- 安全,性能,代码质量等原因导致的技术重构
2. 产品因素
- 产品形态调整(比如由纯PC应用转为响应式应用, 或者从H5到支持跨端)
- 产品业务调整(非常常见的重构理由之一)
- 产品指标调整(如兼容性, 性能指标等导致的代码重构)
上面是我列出来的比较典型的重构场景, 也是我们未来在设计产品技术架构之前需要考虑的方面. 为了提高自己设计的架构稳定性, 我们需要提前和产品沟通明确, 以降低后期重构和维护成本.
最后总结几条架构设计的经验:
- 能做规范的一定要严格做好规范
- 在设计架构之前, 一定要充分理解业务场景, 明确产品的技术交付指标
- 架构设计以可溯源 为基本要求
- 不要盲目追求最好的方案, 以局部最优解为工程设计理念
2. 做项目重构之前, 需要有哪些准备
当然做项目重构也是有技术门槛的,不是所有程序员都能做好重构工作, 建议大家具备如下几种技术能力:
- 对项目所使用的框架语言有相对深入的理解和掌握
- 有一定的前端工程化经验(如webpack, vite, gulp, nodejs, babel, AST等有一定的研究)
- 熟悉常用的web性能优化方案
- 熟悉常见的设计模式和前端编码规范
- 熟悉前端主流的技术框架的设计原理和工程设计思想
接下来我们一起看看常见的几种项目重构场景及其重构方向.
二. 不同类型项目重构的方法论
1. 业务系统自身的重构
业务系统自身的重构一般可以包含如下几个方面:
- 业务代码优化
业务代码优化主要是针对一些核心业务代码, 进行流程上, 逻辑上的重构, 让它更具可读性和维护性, 同时保证业务操作的兼容性, 具体方案如下:
-
复杂业务逻辑需要编写注释
-
代码中访问性属性提供兼容逻辑(常见的比如访问对象属性, a.b.c, 如果a,b为非对象则整段代码将报错)
-
代码结构优化(比如冗长的
if else
或者“回调地狱” 可以采用适配器模式或者es6+
语法来优化) -
方法参数调优(一个函数有多个参数, 可以使用参数对象来提高可读性,降低使用偏差)
-
业务代码性能优化(复杂后台系统比如低代码类产品, 前端需要处理很多数据和逻辑, 此时可以用合适的数据结构和算法优化
js
计算) -
函数式编程思想优化业务函数(可选)
-
业务代码进行单元测试, 提高代码质量(可选)
-
代码规范
早期可能由某名研发单独负责的项目, 对代码规范和格式要求不是很高, 但是需要考虑后期团队扩容带来的协作开发问题, 这个时候如果没有统一的规范, 不同研发小伙伴可能写出的代码千奇百怪, 导致后期维护成本巨大, 尤其是涉及到需要维护他人代码时. 所以我们重构的另一个目标就是降低代码理解成本, 保证项目代码在阅读时就像同一个写出来的, 这样对后期逻辑复用, 组件解耦, 问题定位以及业务代码维护将非常有帮助.
常用的措施有:
- 代码格式规范(如
eslint
) - 逻辑语法类型约束(如
typescript
) - 代码规范(如css命名规范OOCSS, BEM等, 文件命名规范, js变量命名复规范等)
- git 提交规范(常见的是在git hooks的提交阶段对提交格式等进行校验)
当然, 这些都是需要结合自身团队和项目来定的, 这里只做参考.
- 工程化优化
工程化优化主要有以下几个场景:
- 由于业务不断增加, 系统的复杂性加大导致的本地运行和打包速度越来越慢
- 由于项目代码量的增加导致页面臃肿, 需要进行合理的拆分
- 基于已有的工程经验沉淀, 需要对工程化配置做进一步升级, 优化
- 老旧脚手架无法适应当前的项目生产效率
接下来我会针对以上场景, 进行一些解决方案的分享.
- 由于业务不断增加, 系统的复杂性加大导致的本地运行和打包速度越来越慢
针对这种情况, 我们可以借助 speed-measure-webpack-plugin
插件,它可以分析 webpack
的总打包耗时以及每个 plugin
和 loader
的打包耗时,从而让我们对打包时间较长的部分进行针对性优化。
同时默认情况react
, react-dom
, react-router
等公共模块在每次构建都会参与打包, 这些实际上是没有必要的, 我们可以将其传到 cdn
上, 从而减少webpack
的打包”工作量“.
我们可以安装 html-webpack-externals-plugin
来实现将指定模块从打包列表中排除, 具体用法如下:
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
module.exports = {
// ...其他配置代码
plugins: [
new HtmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'https://cdn.dooring.cn/umd/react.production.min.js',
global: 'React',
},
{
module: 'react-dom',
entry:
'https://cdn.dooring.cn/umd/react-dom.production.min.js',
global: 'ReactDOM',
},
],
}),
],
};
为了追求更惊一步的打包效率, 我们可以使用并行的方式构建, 同样 webpack
生态也提供了对应的模块 parallel-webpack
. 具体用法大家可以看文档, 非常简单方便.
其他还有很多优化的方案, 这里我列一下, 大家可以根据实际情况使用:
- 配置并行压缩(
terser-webpack-plugin
,css-minimizer-webpack-plugin
,html-minimizer-webpack-plugin
等都支持parallel
参数) - 预编译资源模块(可以利用
webpack.DllPlugin
来提前将公共模块打包以便后续直接复用) - 使用构建缓存(
webpack5
内置的cache
模块, 或者cache-loader
) - 对打包体积进行分析, 以便有针对性的优化(如
webpack-bundle-analyzer
)
当然除了对已有构建工具的优化, 我们可以评估一下重构成本, 将构建内核替换vite
等更高效的构建工具.
- 由于项目代码量的增加导致页面臃肿, 需要进行合理的拆分
针对项目代码量的增加导致页面臃肿, 我们可以从项目本身的角度, 对项目进行拆解, 将公共模块抽离为公用业务类库或者组件库:
除了对项目进行可复用性拆分之外, 我们还需要根据系统复杂量级, 近一步拆分项目, 比如将一个巨石工程拆分为多个子工程, 单独运行维护, 或者采用之前热点讨论的微前端的模式, 比如使用 qainkun
, single-spa
, Micro App
, EMP
, Garfish
, Bit
这些优秀的微前端框架.
综上, 我们可以根据项目复杂度, 做如下优化:
- 模块 & 组件化
- 拆分子系统(已有架构不变的MPA模式)
- 拆分子系统(微前端)
当然我们始终需要保持一个理念: 局部最优, 误增繁复.
- 基于已有的工程经验沉淀, 需要对工程化配置做进一步升级, 优化
这种情况主要是在项目发展稳定之后, 需要思考的重构方向, 比如早期由于业务场景单一, 很多公共配置都写在业务代码里的, 随着业务复杂之后, 很多模块都需要使用改配置或者变量, 比如:
// a.js
const publicDomain = 'https://dooring.vip';
const serverUrl = 'https://xxx.cn';
// b.js
const publicDomain = 'https://dooring.vip';
// c.js
const appid = 'xxxxxxxx';
const website_Logo = 'http://h5.dooring.cn/logo.png';
对于这种零散且固定的变量, 未来可能会被多个页面或者模块复用, 所以为了降低成本, 我们可以把这些通用配置提取到外层, 作为公共配置文件, 这样后期新项目也能享受开箱即用的配置体验.
拿我的亲身经验, 比如几年前我开发的低代码项目H5-Dooring
, 有一些零散的配置信息分散在项目的各个角落, 后面经过几次重构优化之后, 整个项目只需要在配置文件中轻松配置内容, 即可一键控制页面的走向, 这里分享一下优化过后的配置文件:
// h5-dooring全局配置文件
define: {
START_ENV,
lang,
// 配置h5端访问的域名
h5Domain: 'h5.dooring.cn',
// 设置当前版本号
curVersion: dooringVersion,
// 备案信息
copyright: 'xxxxx-3',
// 是否显示更新弹窗
showUpdateModal: true,
// 更新日志
updateList: [
"1. 新增表格组件",
"2. 国际化优化",
"3. 表单详情页支持内部滚动",
"4. 个人图片库性能优化",
"5. 下载代码功能优化"
],
// 网站logo地址
logo: 'http://cdn.dooring.cn/dr/logo.ff7fc6bb.png',
// 入口页面是否展示赞助品牌和版权提示
showAdsAndTip: true,
// 登录时获取登录码的二维码
qrcode: 'http://cdn.dooring.cn/dr%2Fcode1.png',
// 友情链接展示
friendLinks: [
{
name: 'V6',
link: 'http://v6.dooring.cn/beta',
title: '可视化大屏编辑器'
},
{
name: 'Power',
link: '/powernice',
title: '文档编辑器'
}
],
// 默认语言
defaultLocale: 'zh-CN',
langMap: langMap
},
这样, 我们的工程化结果就可以让不同的技术小伙伴轻松的享受, 让项目创建的成本和自由度得到极大的提升.
这里分享一下 H5-Dooring 的github
地址:
github.com/MrXujiang/h…
- 老旧脚手架无法适应当前的项目生产效率
对于这种场景, 我们就需要对脚手架自身有更多的研究和了解, 比如熟悉webpack
设计思想, 熟悉babel
的工作流程, 熟悉 nodejs
开发工具链的一些模式等, 这里分享几个比较成熟的先进脚手架, 大家如果觉得老项目工程比较老旧, 可以往这几个方向重构:
- 基于webpack5.0的项目脚手架
- vite
- umi4.0
如果大家对以上三种之一比较熟悉, 也可以基于他们二次封装成符合自身业务场景的DIY项目工具.
- 渲染层优化
渲染层优化主要表现在产品的体验上, 比如:
- 提高首屏加载速度
- 白屏体验优化
- 大数据列表渲染优化
- api请求优化
- 动画性能优化
- dom过载导致的页面卡顿优化
代办报建
本公司承接江浙沪报建代办施工许可证。
联系人:张经理,18321657689(微信同号)。
11条评论
楼主说的我也略懂!http://rjuk.cqyiyou.net/test/235217894.html
这个帖子会火的,鉴定完毕!http://7ji.hsa0.com
收藏了,以后可能会用到!http://8n3.yangyupi.com
语言表达流畅,没有冗余,读起来很舒服。http://6ujdtl.fjwxq.com
顶!顶!顶!http://f0ne.3xa3.com
我就搞不明白了,看帖回帖能死人么,居然只有我这么认真的在回帖!http://p6zo3.81iz.com
态度决定一切,不错!http://xkxmg.zhongshenghaitian.com
顶一个!http://f1r6we.bdk6.com
楼主写的很经典!http://www.guangcexing.net/voddetail/vYnDVXFg.html
楼主很有经验啊!http://www.homewarrantyjz.com/
楼主是一个神奇的青年!https://www.wpswe.com/
发表评论