大家好,我是徐晓曦,今天给大家分享一些前端项目重构的思考和回顾,也是我多年来项目研发经验的总结。
一、背景介绍 1、为什么要对项目进行重组
项目重构是每个稳定的互联网企业必经之路。 就像一个产品的诞生一样虚拟项目拆解流程图制作,会经历产品试错和产品迭代。 随着业务或者新技术的不断发展,现有的架构已经不能满足更多业务扩展的需求,所以只有通过重构来“进化”产品,才能跟上时代的快速发展。
这里我根据自己的实际经验总结了项目重构的几个原因:
1、技术因素
技术因素主要包括以下几个方面:
2、产品因素
以上是我列出的典型重构场景,也是我们以后设计产品技术架构时需要考虑的方面。 为了提高我们设计的架构的稳定性,我们需要提前和产品沟通清楚,以减少后期的重构成本和维护成本。
最后总结一下几种架构设计的经验:
2、项目重构前需要做哪些准备
当然,项目重构是有技术门槛的。 并不是所有的程序员都能做好重构。 建议您具备以下技术能力:
接下来我们看一下几种常见的项目重构场景及其重构方向。
2. 不同类型项目重构的方法论
1、业务系统本身的重构
业务系统本身的重构一般包括以下几个方面:
业务代码优化主要是在流程和逻辑上重构一些核心业务代码,使其更具可读性和可维护性,同时保证业务操作的兼容性。 具体计划如下:
前期可能单独负责某个研发团队的项目,对代码规范和格式要求不是很高,但需要考虑后期团队扩张带来的协同开发问题。 如果这个时候没有统一的规范,不同的研发伙伴可能会写出充满怪异的代码,从而导致后期的维护成本巨大,尤其是涉及到维护别人的代码时。 因此,我们重构的另一个目标就是降低代码理解成本,保证项目代码读起来就跟写的一样,以便后期逻辑复用、组件解耦、问题定位和业务代码维护将会非常有帮助。
常用的措施有:
当然,这些都需要结合自己的团队和项目来确定,这里仅供参考。
工程优化主要包括以下场景:
接下来我将分享针对上述场景的一些解决方案。
由于业务不断增加,系统复杂度越来越高,本地运行打包的速度越来越慢
针对这种情况,我们可以使用speed—插件,它可以分析总的打包时间和每笔的打包时间,这样我们就可以对打包时间长的部分进行优化。
同时,默认情况下,react、react-dom、react-等公共模块会在每次构建中参与打包。 这些其实都是不必要的。 我们可以将它们上传到CDN,从而减少打包的“工作量”。
我们可以安装html—将指定模块从包列表中排除,具体用法如下:
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',
},
],
}),
],
};
为了追求更高的打包效率,我们可以并行构建,生态中也提供了相应的模块–。 具体使用可以参考文档,非常简单方便。
其他的优化方案还有很多,我在这里列出来,大家可以根据实际情况使用:
当然,除了对现有构建工具的优化之外,我们还可以评估重构的成本,用vite等更高效的构建工具替换构建内核。
由于项目代码量增加,页面臃肿,需要合理拆分
针对项目代码量增加导致的页面臃肿,我们可以从项目本身的角度对项目进行拆解,将公共模块提取到公共业务类库或组件库中:
除了拆分项目以实现复用之外,我们还需要根据系统的复杂程度对项目进行进一步拆分,比如将一个巨石项目拆分为多个子项目进行单独运维,或者采用之前热议的微项目前端模式,比如使用-spa、Micro App、EMP、Bit这些优秀的微前端框架。
综上,我们可以根据项目的复杂程度进行如下优化:
当然,我们始终需要维护一个概念:局部最优、误差增加、复杂度。
在现有工程经验沉淀的基础上,需要进一步升级优化工程配置
这种情况主要是项目发展稳定后需要考虑重构方向。 比如早期,由于业务场景单一,很多公共配置都写在业务代码中。 随着业务变得更加复杂,很多模块需要使用重新配置或者变量,比如:
// 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-,一些零散的配置信息分散在项目的各个角落。 经过多次重构和优化,整个项目只需要在配置文件中轻松配置内容,就可以一键控制页面的方向。 下面是优化后的配置文件:
// 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
},
这样,我们的工程成果就可以很容易地被不同的技术合作伙伴共享,大大提高了项目创建的成本和自由度。
旧的脚手架无法适应当前的项目生产力
对于这种场景,我们需要对脚手架本身有更多的研究和了解,比如熟悉设计思想,熟悉babel工作流程,熟悉开发工具链的一些模式等等。这里有一些比较成熟和先进的脚手架,大家如果觉得老项目比较老的话,可以按照以下几个方向进行重构:
如果你熟悉以上三者之一,也可以将它们重新打包成符合自己业务场景的DIY项目工具。
渲染层优化主要体现在产品体验上,比如:
提高首屏加载速度。 白屏体验。 优化大数据列表渲染。 优化API请求。 优化动画性能。 优化dom过载导致的页面卡顿。
以上是我之前遇到过的渲染优化的一些维度,接下来我会一一介绍解决思路。
1.提高首屏加载速度
有很多方法可以帮助我们提高首屏加载速度,比如:
当然,还有很多客观因素。 例如,用户所在区域是网络环境较弱的地区。 我们可以根据网络速度提供最小化弱网络的替代页面,以确保我们网站的可用性和可访问性。
2.白屏体验优化
对于白屏优化,也有很多成熟的例子,比如骨架屏的使用:
如果我们的项目是基于vue-cli构建的,我们可以使用更成熟的page—方案,否则我们仍然可以选择vue–提供的vue–。
当然,你的项目使用了react,你也可以轻松使用react–等svg解决方案来定制属于自己的骨架屏。
除了骨架屏之外,我们还可以提供模板页面或者加载动画,在页面加载前给用户一个优雅的过渡提示。 例如:
3.大数据列表渲染优化
对于中后台一些复杂的系统模块,可能会涉及到一次性渲染大量列表项或多级组织树:
尤其是在大公司或者大集团中,出现的频率非常高。 在这种情况下,我们需要使用虚拟列表或延迟加载节点。 虚拟列表应用广泛,目前有几种成熟的解决方案可以直接使用:
如果您的项目当前没有使用此类解决方案,您不妨评估是否可以使用这些解决方案来保护您的项目。
4. API请求优化
API请求优化主要针对这样一种场景:页面的渲染取决于一个或部分请求的完成,或者因为某个页面的请求量较大,每次重新进入该页面都需要一定的性能高架。
对于这两种情况,不仅影响浏览器渲染,而且大大增加了服务器的压力,所以我们需要对请求或者页面进行一定程度的缓存。
例如,我们可以将长期不变的请求数据存储起来,第二次访问时可以直接从中获取请求数据,这样既减轻了服务器的压力,又提高了效率第二次渲染。
其次,我们可以对部分页面做路由缓存,避免每次切换时重新渲染。 当然,这只是针对不需要实时更新数据的页面。 之前我也分享过一篇浏览器缓存接口实战的文章。 谢谢,有兴趣的可以学习参考一下。 为此,我封装了一个开箱即用的库,您可以直接使用:
地址:
5.动画性能优化
这也是一个老生常谈的问题了,下面直接分享几个解决方案:
由于dom动画的上限很低,对于一些比较复杂的动画渲染,我们可以使用svg或者代替,以减少dom对浏览器的内存占用。
6、dom过载导致的页面冻结优化
如果一个页面的dom太多,就会带来很多问题。 一方面会让浏览器内存占用过高,导致其他不相关的js逻辑操作被阻塞或无效,表现为页面卡顿或无响应。
为了解决这个问题,我们仍然可以使用虚拟滚动方案或者延迟加载方案来保证用户当前屏幕下的dom在合理的范围内。 如果不可避免要显示大段的dom元素,我们可以使用单独的页面来托管或嵌入,以避免页面其他部分的宕机。 还可以部分“冻结”复杂的dom(将其转换为非活动状态下的图片,并在激活时逐渐渲染)
产品需求引发的重构主要场景如下:
当然,场景还有很多,这里就不一一介绍了。 上面列出的场景都比较常见,解决方案也有很多。 稍后我会一一回顾。 情况也是需要考虑的,毕竟是一个工作量很大的任务。
2、技术升级带来的重组
技术升级带来的重构主要包括前端框架的升级、前端设计模式的升级、脚手架的升级。 后两者主要是围绕前端技术的不断演进。 我们采用程序化的升级方式,比如从传统的gulp升级到vite,或者从升级到vite等。前者是比较常见的场景,企业中有很多老系统,采用比较传统的技术方案比如CMD模式+,但是新项目使用了+vue或者react,这时候我们就需要项目状态来选择性的做重构:
老项目几乎不需要维护的情况
在这种情况下,我们不需要彻底重写新的框架。 我们只需要在重构时对旧项目代码进行足够的注释并封装类库即可:
其次,我们需要做好js变量隔离,因为传统模式下我们会在顶层定义大量var全局变量,作为优化的一部分,我们可以使用闭包自执行和变量约定标准化我的js变量定义以防止全局变量污染。
老项目还是需要不断迭代,后面还会有新的模块
这种情况下,我们就需要进行评估和拆分。 如果是小模块,我们可以使用插件来快速迭代。 如果是页面级迭代,且交互比较复杂虚拟项目拆解流程图制作,我们可以将旧系统的新页面独立成一个子工程,使用最新的框架(如vue)进行迭代开发,然后与旧系统集成通过 MPA 系统:
老项目和新项目需要互相通信、嵌套
这种场景最好的方式是使用 + ,或者我们可以参考类似的微前端方法来管理和组织不同的子系统。
3.组件库重构
对于一个包含很多子系统的复杂项目系统,设计一个好的架构的第一步就是要合理划分组件,并且组件的粒度划分得足够细,这样才能最大程度地复用组件。
对于任何复杂的系统来说,最重要的是实现错综复杂的业务功能,但不同模块或子系统之间的很多业务往往是相互关联或相似的。 重复编写业务代码完全没有必要,而且对可维护性也是一种打击,所以基于这种场景,我们的业务组件的出现是有必要的。 我们可以将具有相似功能或需求的有机体封装成一个业务组件,并暴露接口以实现灵活的可定制性,从而可以在不同页面的不同子系统中复用相同的逻辑和功能。
同样,具有相同或相似视觉或交互特征的块可能会出现在不同的页面上。 为了提高复用性和开发效率,我们经常基于基础组件和业务组件进行重新封装,使其成为独立的块,直接复用。
通过这样的层层封装,我们逐渐构建了一套完整的组件化系统。 基于这种模型的开发往往是一个好的前端架构的开始。 但需要注意的一点是,高层组件必须依赖于低层组件,但低层组件不能包含高层组件。 (听起来有点像Rudex的单向数据流规则),他们的关系如下图:
因此,组件库的重构需要对我们的项目有本质的了解,并对页面进行有效的拆分,从而实现局部优化,减少后续的维护成本,提高整个系统甚至跨系统的性能。 重复使用。
我写过一篇详细的文章,教你如何从0到1搭建前端团队的组件体系,你可以参考一下。
总结
系统重构是一个持续的过程。 我们不仅要有不断学习的态度,还需要不断实践,积累优秀的最佳实践,让我们的系统在不断重构的过程中不断适应复杂多变的“社会”。 环境”。
