一点背景,电商中心去年重构了之前纷繁复杂的各类商品购买页面,刚开始的时候,相对于老商品页,统一商品页打开速度得到肉眼可见的提升,但后面随着各种项目迭代,首屏速度逐渐变慢。
早期的时候,使用手动埋点上报各关键指标数据,后面使用腾讯出品的RUM对页面实现更完善的监测。
由于这个项目已经发布到现网,所以无法重现之前没那么快的效果。
之前在各种技术社区也看了各种关于页面性能优化的相关文章,但个人觉得共性太强,不一定每个公司的业务都能直接套用。
比如有的建议是一些依赖可以用公共CDN的,这要是CDN炸了,肯定会连带导致公司的正常业务受影响。我觉得对于这些文章的态度是可以看,但具体怎么用还是得看是否适合自己业务,包括我现在这篇。
另外,各个场景的优化思路也不一样,比如商品详情页,比如直播页,比如订单列表页。商详页个人认为异于其他页面的在于,有各种组件,有营销活动氛围,有大量图片。
而直播页,直播的画面这一点是其他页面都没有的,首屏速度可能就很大程度掣肘于直播画面的显示。
优化思路
动手优化之前,得想明白优化的目标是啥,这个很简单,比快更快嘛。但其实如果是对已有项目优化的话,会受限与现有的技术方案。比如京东的手机端页面,每看一遍都要感叹一下是真的快,以至于会让你觉得,到底是否有刷新。
所以刚刚讲定目标很简单,其实也没有简单,如果本身业务是CSR,但是对比的目标却是SSR,我觉得几无可能实现。定目标一定是跟自己比,不是跟别人比,至少不是全盘跟别人比。
有了优化目标之后,还得有优化思路。都知道服务端渲染是最快的,那为什么服务端渲染是最快的,因为页面的内容都在返回给浏览器之前填充好了,而不是依赖脚本在浏览器中创建。
总的来说,优化又还分两个方向,一是后端对请求响应上的优化,二是前端对内容渲染的优化。
前者对于小鹅商详页来说,主要体现在模板和接口的响应时间,如下图,TTFB 这一指标除个别性能较差接口外,平均在160ms左右,并不能算快。
而后者,就是本文主要分享的内容。
结合上面所说,可以发现,于前端而言,优化的总体思路不外乎这三点:
- 必须加载的页面内容,处理好加载的优先级
- 必须加载的内容,尽可能做到最快的呈现
- 能不加载/请求的,就不加载/请求
除此之外,优化也还分下述三个阶段的优化:
- 代码层面的优化
- 项目打包的优化
- 项目部署的优化
优化项
组件按需引入
这一点可能是本次优化中提速最多的一个,因为商详页有很多公用组件也有很多业务组件,之前开发时没多细想,都是按如下代码一样一股脑的按需引入。
components: {
// 评价
GoodsComment: () => import(/* webpackChunkName: "GoodsComment" */ "./components/GoodsComment"),
}
但这样并不全都对首屏速度有增益。也很好理解,比如主图模块,所有商品类型都会有主图,那其实就没有必要按需了。不按需的话,直接用原来引入组件的方式就好了。
那与主图模块类似,在这次优化中,顶部栏、价格模块、标题模块、商品介绍模块和底部按钮模块都删除了按需加载的逻辑。
这样的结果就是不按需的组件会被打包在app.js
文件中,这样组件渲染的优先级是非常高的。请求完模块之后立马就会开始这些关键组件的DOM渲染。
其实,这就有点像对服务端的渲染的模拟了,目的还是尽可能快的将必要的 DOM 渲染好。
另外,数据与模板绘制的解耦,也能一定程度上增益首屏速度。比如一些共享的关键数据,不要放在组件内部请求,而是统一请求后放在store中按需使用。
这也很好理解,还是主图模块,他只需要一个图片的链接,那完全没有必要在组件内发起一个对于图片链接的请求(我们当然不会这样做,这里只是举一个极端些的例子方便理解),最好的情况就是组件的DOM的渲染和数据的请求同时发生,这样就省掉了组件内逻辑执行的时间。
v-if 和 v-show 的合理使用
这一项优化的重点在于合理,v-if和v-show的区别就不展开,写前端的应该都清楚。在商详页的页面结构中,由于是客户端渲染,需要等待接口的时间,就不可避免地会出现白屏。一般来说,这段白屏时间,都会使用一些loading效果来优化用户体验。
但其实白屏的这段时间仍然可以得到利用,白屏只是意味着接口数据还没返回,但并不影响DOM结构的渲染。那对应到代码上,就应该使用v-show
而不是v-if
。
<loading v-if="loading"></loading>
<div class="component-wrap" v-show="!loading">
<!-- component code -->
</div>
对于这一项,还可以再举个例子,在左下角的图标区域中,有些图标是点击产生弹窗的。比如客服还有课程目录,那在图标本身都不渲染的情况下,图标对应的弹窗更是没有渲染。所以可以在响应的组件上加上v-if
判断,减少不必要的 DOM 渲染。
图片的压缩
这一点在商详页的场景下显得尤为重要。主图模块还有商品介绍模块会有大量的图片使用,但是有的商家直接传 7-8 MB的原图,这就很难受。
对此,因为图片都是存在腾讯COS桶中,可以很好的利用CDN+数据万象的能力实现对图片压缩以及获取速度的优化。
结合上面的说到的按需加载,虚拟类商品的介绍会有多项Tab,但首屏只展示介绍,所以没有必要将别的比如目录组件打包进首屏请求的内容中,等用户点击后再请求。
另外在
HTTP协议升级
HTTP 1.1 对链接数是有限制的,这意味着会产生请求的等待,无论是js文件还是api 请求。升级到HTTP 2,就不会面临这样的问题了。
gzip压缩
辅助工具
webpack-bundle-analyzer
这个工具可以用图形化的界面来分析工程的依赖包大小,如下图,是goods-fe目前的。
用vue-cli创建的项目已经内置了这个功能,可以通过vue-cli-service build --report
命令来使用。
RUM
在早之前,首屏各性能指标都是通过手动埋点来获取,数据较为单一,无法从更多角度来监控页面性能。
后面接入了腾讯的RUM后,主要使用RUM来实现对首屏数据的获取,具体使用,这里就不展开。
从RUM监测数据得到的一点结论。
- 样本量越大,得到的数据越准确
- 样本量小,各项数据容易被极值影响导致忽大忽小。但更多的是忽大,因为页面可以有各种原因打开变慢,但是变快只有两种可能(网速够快,页面性能好)。
- 这一点可以从下图很容易得出结论,可以看到,当样本量显著多于其他时间时,首屏时间的平均结果越接近真实水平。