2023年通天塔搭建页前端性能优化阶段分享
Tech导读
本文分享京东集团2023年通天塔项目在搭建页前端性能优化的经验和成果。读者将了解如何通过减少页面加载时间、提高交互响应速度和执行效率,来显著提升用户体验。文章还将涵盖对工具和实践方法的评估,以及如何量化性能改进的效果。
导读
本文分享京东集团2023年通天塔项目在搭建页前端性能优化的经验和成果。读者将了解如何通过减少页面加载时间、提高交互响应速度和执行效率,来显著提升用户体验。文章还将涵盖对工具和实践方法的评估,以及如何量化性能改进的效果。
01 前言
在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!
通天塔搭建页项目是用来搭建各类活动页面,比较老且业务复杂的项目,可优化点还是非常多的。今年侧重对运营页首屏加载的性能优化,在保证系统稳定可控、需求持续迭代前提下,最终提升了58.8%速度。在此非常感谢通天塔产品组、后端组、前端组同学,对项目性能优化大力支持。
市面上有很多性能优化方案,数不胜数,但如果开始就只是模仿一些边边角的优化,虽然也会略有效果,但不一定能给系统解决核心卡顿问题,不能给系统带来质的提升,甚至到后面优化效果越来越差,这些优化可能会冲突、可能变成负优化。而且如果不去优先分析核心的问题,可能就会一直忽视核心问题,导致系统长期处于低效低体验的状态,随着业务复杂变得越来越卡。
02
常见分析工具
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
图 2.
分析网页在运行时(而不是加载)时的性能。具体分析页面每个阶段哪些方法在执行,哪些资源在加载,很容易看出哪里在阻塞。
也可以用来设置不同的cpu和网络环境。由于开发者电脑一般性能比较好,模拟较差性能和网络也是有必要的。
图 3.
1)先看摘要Summary标签页,分析到底是加载、执行脚本、渲染、绘制哪一个最占时间,再去决定主要优化方向。
https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing?utm_source=devtools&hl=zh-cn#avoid-forced-synchronous-layouts
图 5.
2)再展开主要Main部分。开发者工具会向您显示主线程上的活动随时间变化的火焰图。x 轴表示一段时间内的记录。每个条形都代表一个事件。横条越宽,表示事件用时越长。y 轴表示调用堆栈。如果您看到事件相互堆叠,则表示上层事件导致了下层事件。
3)放大上方Overview,找到任务右上角的红色三角形,定位到耗时较长的任务,再定位到具体的方法中。
对于长任务,官网还有进一步优化建议:
懒加载相关建议:
4、代码覆盖率Coverage
可以分析不同资源使用率情况。对于低使用率且较大体积的文件,考虑懒加载、移除无效代码。
5、灯塔面板Lighthouse
https://developer.chrome.com/docs/lighthouse/overview?hl=zh-cn
主要是给页面综合评分,还有FCP、LCP、TBT、CLS分析。如果评分还有不足的地方,那就还有较大优化的空间。
03 主要优化方向
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
3.1 加速请求资源速度
个人体验感觉,缓存带给页面性能提升是最明显的。
像base64的图片可以当数据类存储。
还有函数内部的缓存;
<link rel="dns-prefetch" href="//cdn1.jd.com">
<link rel="dns-prefetch" href="//cdn2.jd.com">
<link rel="dns-prefetch" href="//cdn3.jd.com">
比如字体文件
<link rel="preload" as="font" href="xxxx.woff" />
不止是静态资源,有条件的话,尽量让所有资源都走CDN,有效提升加载速度,减少白屏时间。
3.2 减少请求资源大小
import * as xxxx-ui from 'xxxx-ui'
const Button = xxxx-ui['Button']
最好都改成按需加载,确保打包时候不会把整个库都打进去。
import { Button } from 'xxxx-ui'
只要支持基于 ES modules 的 tree shaking,就会有按需加载的效果。
不过有的引入比较隐蔽,不容易直接发现,只有进行打包分析后才能实际发现多种情况打包了多余的包。
(b)引入子包写法不正确
比如引入加密包不正确,引入导致打包体积很大。
import crypto from 'crypto'
const str = 'xxxxxxxxx'
const sign = crypto.createHash('md5').update(str).digest('hex')
实际只用了crypto的md5方法,但却把整个crypto包和其依赖的包都打包进代码里。
后续改成只引入crypto的md5方法就能起到一样的效果。
import md5 from 'crypto-js/md5'
const str = 'xxxxxxxxx'
const sign = md5(str).toString()
类似的lodash也有这样的情况,通过改变写法,也可以改善打包大小,在Gzipped下从几十KB减少到几KB。
只需改写下,单个引入lodash里的方法
图 13、14.
比如,antd3.0的组件引用也有类似问题,需要改写引用代码按需加载组件;
// 引入编辑器样式
import 'braft-editor/dist/index.css'
import Header from './Component/Header'
<Header />
const Header = React.lazy(() => import(/* webpackChunkName:"Header" */ './Component/Header'))
<React.Suspense fallback="">
<Header />
</React.Suspense>
注意1:还是不能滥用,主要针对低优先级或者体积过大的模块、组件。
setTimeout(() => import(/* webpackChunkName: 'xxx'*/ './xxx.less' ), 500)
比如,使用 Day.js 替换 momentjs 优化打包大小,但是打包多一个配置,这是antd里介绍的配置;
<script src="https://cdn1.jd.com/xxx.js" async></script>
比如这个生成excel文件的插件,打包在项目里就会很重。放在cdn上,不随项目打包版本更新,每次加载后还能有较长缓存时间。
- 直接压缩工具压缩;
- 对象存储(OSS)压缩或者裁剪图片;
- nginx服务器中配置;
多数语言包是不需要被打包进来的,可以打包分析检查一遍。
图 19.
8)字体文件压缩
一个完整字体文件都有几MB,而一般项目里只有少数文本需要用到特殊字体,可以利用类似Fontmin把需要的文字单独拎出来。
如果字数较少也考虑图片替代,和其他图片合并。
9)打包移除多余代码
(a)Tree Shaking 删除多余代码
webpack3可以配置,webpack4+的mode: procution下自带。其他打包工具也有支持。
注意1:使用 ES2015 模块语法(即 import 和 export)。
注意2:确保没有编译器将 ES2015 模块语法转换为 CommonJS。比如lodash就是基于commonjs,所以才有上文《减少请求资源大小》中把整个lodash包都打进去的情况。
注意3:在项目的 package.json 文件中添加 "sideEffects" 属性。
https://webpack.docschina.org/guides/tree-shaking/
(b)移除生产环境的 console.log、debugger、注释
new UglifyJsPlugin({
uglifyOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
})
10)生产环境选择适当source-map方案,控制打包体积
根据实际情况,需要考虑源代码要不要隐藏,调试要不要更友好。
3.3 减少请求资源数量
合并js请求:配置webpack里splitChunks的cacheGroups,把必用的公共依赖打包到一起,类似:
(a)多个图片合并成雪碧图;
(b)图标类图片尽量使用矢量图标库iconfont(尽量按需配置、按需打包);
(c)可css、svg绘制的,尽量用css、svg实现;
<img loading="lazy" src="image.jpg" alt="..." />
3.4 优化代码逻辑
(a)有些数据会非常大,尽量分页优先请求或者加载渲染涵盖可视区域范围前几个、前十几个,让首屏的展示速度更快一点,后续再用完整数据覆盖或者增量渲染;
(b)非权限类的请求可以放在更早的位置发出,非阻塞性的请求可以先请求再等较晚时候处理逻辑,业务优先级低的请求可以放在页面渲染完成以后发出;
(c)不同优先级的组件,可以分不同阶段加载、处理逻辑,让关键模块优先渲染、页面整体过渡效果更好;
(d)如果存在前置的页面,可以在前置的页面空闲时间提前加载后续页面的数据,甚至是资源;
(e)复杂的渲染和数据处理,也可以考虑迁移到服务端来做;
(a)循环、递归嵌套层级太深太多的话很容易造成卡顿;
(b)循环使用时,确认是否可以提前中断循环,而不是把每个循环都走完;
(b)有的方法比较耗费性能,类似深拷贝、字符串拼接,注意使用次数;
(c)检视下手写的方法是否可以用lodash等成熟库的方法替代,可能性能更好;
(d)不同模块里相同或者相似的代码,提取成公共方法或者组件;
(e)监听器、计时器最好控制数量,配套退出机制,及时清除;
3.5 优化业务逻辑
(1)对于大部分项目,其实已经做过或多或少的优化,但可能依然有卡顿。
(2)对于大部分文章,其实主要集中于纯技术,也都很少去思考产品规划是否合理、业务逻辑是否合理。
(a)针对大多数用户使用习惯,优先提供简化易懂易用的常用交互,同时把专业垂直复杂的冷门交互单独拎出来;
(b)有的业务模块逻辑非常重的,可以独立出来,或者拆分成多级、多个模块;
(c)对于功能繁杂的页面,应该把不重要的业务模块迁出、收起、延后展示;
(d)通过埋点确认没人使用的业务模块,应该考虑下线;
(e)低使用频率的功能控制加载次数,类似版本更新功能,每次打开页面都会请求最新配置接口数据,可以设置间隔一段时间才能重新请求,或者跟随版本号更新后才能重新请求一次;
04 总结
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
通天塔搭建页是用来搭建京东商城各类活动页面,整个项目迭代时间很久远且业务复杂。随着体量上升,加载速度慢、操作卡顿现象越来越多,性能优化成了23年的当务之急。通天塔及互动前端组经过一年的全方位优化,提升了58.8%首屏加载速度,兼顾了体验和功能,在此分享一下这一年的经验和心得。
求分享
求点赞
求在看