在线客服

前端项目重构的一些思考和复盘

adminadmin 报建百科 2024-04-25 82 8
前端项目重构的一些思考和复盘

首发: 趣谈前端

作者: 徐小夕

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的提交阶段对提交格式等进行校验)

当然, 这些都是需要结合自身团队和项目来定的, 这里只做参考.

  • 工程化优化

工程化优化主要有以下几个场景:

  • 由于业务不断增加, 系统的复杂性加大导致的本地运行和打包速度越来越慢
  • 由于项目代码量的增加导致页面臃肿, 需要进行合理的拆分
  • 基于已有的工程经验沉淀, 需要对工程化配置做进一步升级, 优化
  • 老旧脚手架无法适应当前的项目生产效率

接下来我会针对以上场景, 进行一些解决方案的分享.

  1. 由于业务不断增加, 系统的复杂性加大导致的本地运行和打包速度越来越慢

针对这种情况, 我们可以借助 speed-measure-webpack-plugin 插件,它可以分析 webpack 的总打包耗时以及每个 pluginloader 的打包耗时,从而让我们对打包时间较长的部分进行针对性优化。

同时默认情况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等更高效的构建工具.

  1. 由于项目代码量的增加导致页面臃肿, 需要进行合理的拆分

针对项目代码量的增加导致页面臃肿, 我们可以从项目本身的角度, 对项目进行拆解, 将公共模块抽离为公用业务类库或者组件库:

除了对项目进行可复用性拆分之外, 我们还需要根据系统复杂量级, 近一步拆分项目, 比如将一个巨石工程拆分为多个子工程, 单独运行维护, 或者采用之前热点讨论的微前端的模式, 比如使用 qainkun, single-spa, Micro App, EMP, Garfish, Bit 这些优秀的微前端框架.

综上, 我们可以根据项目复杂度, 做如下优化:

  • 模块 & 组件化
  • 拆分子系统(已有架构不变的MPA模式)
  • 拆分子系统(微前端)

当然我们始终需要保持一个理念: 局部最优, 误增繁复.

  1. 基于已有的工程经验沉淀, 需要对工程化配置做进一步升级, 优化

这种情况主要是在项目发展稳定之后, 需要思考的重构方向, 比如早期由于业务场景单一, 很多公共配置都写在业务代码里的, 随着业务复杂之后, 很多模块都需要使用改配置或者变量, 比如:

// 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…

  1. 老旧脚手架无法适应当前的项目生产效率

对于这种场景, 我们就需要对脚手架自身有更多的研究和了解, 比如熟悉webpack设计思想, 熟悉babel的工作流程, 熟悉 nodejs 开发工具链的一些模式等, 这里分享几个比较成熟的先进脚手架, 大家如果觉得老项目工程比较老旧, 可以往这几个方向重构:

  • 基于webpack5.0的项目脚手架
  • vite
  • umi4.0

如果大家对以上三种之一比较熟悉, 也可以基于他们二次封装成符合自身业务场景的DIY项目工具.

  • 渲染层优化

渲染层优化主要表现在产品的体验上, 比如:

  1. 提高首屏加载速度
  2. 白屏体验优化
  3. 大数据列表渲染优化
  4. api请求优化
  5. 动画性能优化
  6. dom过载导致的页面卡顿优化

代办报建

本公司承接江浙沪报建代办施工许可证。
联系人:张经理,18321657689(微信同号)。

喜欢0发布评论

8条评论

  • 游客 发表于 2个月前

    楼主说的我也略懂!http://rjuk.cqyiyou.net/test/235217894.html

  • 大发快三welcome 发表于 1周前

    我就搞不明白了,看帖回帖能死人么,居然只有我这么认真的在回帖!http://p6zo3.81iz.com

发表评论

  • 昵称(必填)
  • 邮箱
  • 网址