来源:京东技术
导读
本文分享京东集团2023年通天塔项目在搭建页前端性能优化的经验和成果。读者将了解如何通过减少页面加载时间、提高交互响应速度和执行效率,来显著提升用户体验。文章还将涵盖对工具和实践方法的评估,以及如何量化性能改进的效果。
本文分享京东集团2023年通天塔项目在搭建页前端性能优化的经验和成果。读者将了解如何通过减少页面加载时间、提高交互响应速度和执行效率,来显著提升用户体验。文章还将涵盖对工具和实践方法的评估,以及如何量化性能改进的效果。
通天塔搭建页项目是用来搭建各类活动页面,比较老且业务复杂的项目,可优化点还是非常多的。今年侧重对运营页首屏加载的性能优化,在保证系统稳定可控、需求持续迭代前提下,最终提升了58.8%速度。在此非常感谢通天塔产品组、后端组、前端组同学,对项目性能优化大力支持。
图 1.市面上有很多性能优化方案,数不胜数,但如果开始就只是模仿一些边边角的优化,虽然也会略有效果,但不一定能给系统解决核心卡顿问题,不能给系统带来质的提升,甚至到后面优化效果越来越差,这些优化可能会冲突、可能变成负优化。而且如果不去优先分析核心的问题,可能就会一直忽视核心问题,导致系统长期处于低效低体验的状态,随着业务复杂变得越来越卡。
常见分析工具

图 2.
分析网页在运行时(而不是加载)时的性能。具体分析页面每个阶段哪些方法在执行,哪些资源在加载,很容易看出哪里在阻塞。
也可以用来设置不同的cpu和网络环境。由于开发者电脑一般性能比较好,模拟较差性能和网络也是有必要的。

图 3.
1)先看摘要Summary标签页,分析到底是加载、执行脚本、渲染、绘制哪一个最占时间,再去决定主要优化方向。
图 4.
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 轴表示调用堆栈。如果您看到事件相互堆叠,则表示上层事件导致了下层事件。
图 6.
3)放大上方Overview,找到任务右上角的红色三角形,定位到耗时较长的任务,再定位到具体的方法中。

图 7、8.
对于长任务,官网还有进一步优化建议:
懒加载相关建议:

4、代码覆盖率Coverage
可以分析不同资源使用率情况。对于低使用率且较大体积的文件,考虑懒加载、移除无效代码。
图 9.
5、灯塔面板Lighthouse
https://developer.chrome.com/docs/lighthouse/overview?hl=zh-cn
图 10.
主要是给页面综合评分,还有FCP、LCP、TBT、CLS分析。如果评分还有不足的地方,那就还有较大优化的空间。
3.1 加速请求资源速度
个人体验感觉,缓存带给页面性能提升是最明显的。
像base64的图片可以当数据类存储。
还有函数内部的缓存;
比如字体文件
不止是静态资源,有条件的话,尽量让所有资源都走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包和其依赖的包都打包进代码里。
图 11.
后续改成只引入crypto的md5方法就能起到一样的效果。
import md5 from 'crypto-js/md5' const str = 'xxxxxxxxx'const sign = md5(str).toString()
类似的lodash也有这样的情况,通过改变写法,也可以改善打包大小,在Gzipped下从几十KB减少到几KB。
图 12.
只需改写下,单个引入lodash里的方法


图 13、14.
比如,antd3.0的组件引用也有类似问题,需要改写引用代码按需加载组件;
图 15.
// 引入编辑器样式import 'braft-editor/dist/index.css'

import Header from './Component/Header'
const Header = React.lazy(() => import(/* webpackChunkName:"Header" */ './Component/Header'))
注意1:还是不能滥用,主要针对低优先级或者体积过大的模块、组件。
setTimeout(() => import(/* webpackChunkName: 'xxx'*/ './xxx.less' ), 500)
比如,使用 Day.js 替换 momentjs 优化打包大小,但是打包多一个配置,这是antd里介绍的配置;
图 17.
比如这个生成excel文件的插件,打包在项目里就会很重。放在cdn上,不随项目打包版本更新,每次加载后还能有较长缓存时间。
图 18.
- 直接压缩工具压缩;
- 对象存储(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,把必用的公共依赖打包到一起,类似:
图 20.
(a)多个图片合并成雪碧图;
(b)图标类图片尽量使用矢量图标库iconfont(尽量按需配置、按需打包);
(c)可css、svg绘制的,尽量用css、svg实现;
3.4 优化代码逻辑
(a)有些数据会非常大,尽量分页优先请求或者加载渲染涵盖可视区域范围前几个、前十几个,让首屏的展示速度更快一点,后续再用完整数据覆盖或者增量渲染;
(b)非权限类的请求可以放在更早的位置发出,非阻塞性的请求可以先请求再等较晚时候处理逻辑,业务优先级低的请求可以放在页面渲染完成以后发出;
(c)不同优先级的组件,可以分不同阶段加载、处理逻辑,让关键模块优先渲染、页面整体过渡效果更好;
(d)如果存在前置的页面,可以在前置的页面空闲时间提前加载后续页面的数据,甚至是资源;
(e)复杂的渲染和数据处理,也可以考虑迁移到服务端来做;
(a)循环、递归嵌套层级太深太多的话很容易造成卡顿;
(b)循环使用时,确认是否可以提前中断循环,而不是把每个循环都走完;
(b)有的方法比较耗费性能,类似深拷贝、字符串拼接,注意使用次数;
(c)检视下手写的方法是否可以用lodash等成熟库的方法替代,可能性能更好;
(d)不同模块里相同或者相似的代码,提取成公共方法或者组件;
(e)监听器、计时器最好控制数量,配套退出机制,及时清除;
3.5 优化业务逻辑
(1)对于大部分项目,其实已经做过或多或少的优化,但可能依然有卡顿。
(2)对于大部分文章,其实主要集中于纯技术,也都很少去思考产品规划是否合理、业务逻辑是否合理。
(a)针对大多数用户使用习惯,优先提供简化易懂易用的常用交互,同时把专业垂直复杂的冷门交互单独拎出来;
(b)有的业务模块逻辑非常重的,可以独立出来,或者拆分成多级、多个模块;
(c)对于功能繁杂的页面,应该把不重要的业务模块迁出、收起、延后展示;
(d)通过埋点确认没人使用的业务模块,应该考虑下线;
(e)低使用频率的功能控制加载次数,类似版本更新功能,每次打开页面都会请求最新配置接口数据,可以设置间隔一段时间才能重新请求,或者跟随版本号更新后才能重新请求一次;
通天塔搭建页是用来搭建京东商城各类活动页面,整个项目迭代时间很久远且业务复杂。随着体量上升,加载速度慢、操作卡顿现象越来越多,性能优化成了23年的当务之急。通天塔及互动前端组经过一年的全方位优化,提升了58.8%首屏加载速度,兼顾了体验和功能,在此分享一下这一年的经验和心得。