如何从零开始做性能优化
性能优化作为一个老生常谈的项目,能在网上看到各种优化点,但对于一个新手来说,要着手一个完整的性能优化项目,却不知从何下手。这篇文章将介绍如何从零开始做性能优化,介绍如何将性能优化项目落地、推进、实施,对业务赋能等。
1. 先来一个面试
首先,先来一个面试题:请你介绍下前端性能优化?(不妨在浏览下方内容前,先自我回答下这个问题。)
在实际面试他人过程中,问到这个题目时,往往都没得到一个满意的答案,大部分的人上来就是图片懒加载,css 压缩,js 压缩之类的内容,其实没错,但也可以知道对方是没有做过系统性的性能优化的。
2. 文档目录
那么怎么样的才算是系统性的性能优化呢?看完这个文章就知道了。
本文将从以下四个内容进行介绍:
为什么要做?(为什么需要对项目做性能优化,前端性能优化的价值在哪里?)
如何做?(如何做一个完整的性能优化,如何下手?)
最佳实践(网易严选业务中比较有效且特殊的策略分享)
结语(整个内容的回顾)
3. 为什么要做?
在做性能优化这个事项之前,我们得先知道我们做的这个事情是否有价值。
3.1 用户体验金字塔
先来看一个用户体验金字塔,大家觉得对用户体验影响最大的内容是啥?
页面的颜色? 页面的信息?其实都不是,肯定是性能优化相关的内容,因为本篇文章的主题就是性能优化。
从 google i/o 大会上,能看到对用户体验造成最大影响的就是页面的加载时长。这个其实也很好理解,你的页面做得非常精美,但是前置的流程是需要用户能看到这个页面。
3.2 性能优化业务价值
页面加载时长对用户体验有影响,那么对业务数据是否有影响呢?
答案是肯定的,页面加载时长越长,越容易造成用户流失,超过 5s,90% 的用户就会离开。
可以想象一下,你手上的业务如果页面加载时长缩短几百毫秒,甚至更多,是不是对业务也会起到正向的作用呢?
4. 如何做?
既然性能体验优化是非常有价值的,那么我们该如何来做呢?
4.1 意识阶段
在讲具体怎么做之前,先说下性能优化的意识状态。
在正式启动性能优化之前,一般都处于无意识状态,面试中,往往回答的也都是这类答案。在系统化的性能优化中,我们需要优先改变自己的意识状态,进阶到状态 3 和状态 4 中,状态 3、4 需要我们关注指标,关注数据。
强调下,性能优化的首要基础是指标和数据。
4.2 性能体验提升流程
在意识形态改变以后,性能体验提升流程可以分为以下 4 个内容:
建设度量体系(确定性能衡量指标,建设性能监控平台)
制定目标(度量体系接入&现状数据分析)
性能优化实施(团队组织和项目推进,产出优化策略)
成果验收和复盘
4.2.1 建设度量体系
首先我们来建设衡量页面的度量体系。度量体系分成衡量指标和监控平台。
那用什么指标来衡量这个页面的性能呢?我们说这个页面很快或者很卡,都是基于主观的一个感受,这个是无法量化的。在项目实施中也无法说我对这个页面进行了优化,这个页面变快了,无法量化的数据也无法给你合理的评分。
现在 okr 很流行,很重要的点就是量化,同理,对于页面性能我们也需要一个量化的值来表示页面性能。
4.2.1.1 页面性能衡量指标三个阶段
实际上,页面性能衡量指标经历了 3 个阶段,围绕的都是首屏时间。
第一阶段:自定义打点时期
页头和首屏 dom 处分别通过 new Date() 打点,方法笨重,精确度不高,数据单一。
第二阶段:W3C标准时期
后面就进入了第二阶段,W3C 标准时期,通过 performance api 来做计算,但是随着单页的流行,load 时间早于首屏时间,W3C 标准失去了原先的意义。
第三阶段:感官性能优化指标
到了现阶段,引入了感官性能优化指标。主要分成 FP、FCP、FMP、TTI 等数据指标,其中比较符合首屏时间的就是 FMP 指标,可惜没有直接的系统 api 提供。
新版本提供了LCP,类似于FMP的时间,但存在兼容性,而对于性能这个事项,慢网络和老设备更是应该关心的点。
既然没有直接的 api 提供,那么可以大胆地猜想首屏中主要元素的加载完成时间类似的等同于 FMP 时间。
4.2.1.2 捕获FMP原理
成为FMP元素的条件
首屏中,怎么样的元素才可以成为主要元素呢?
看截图,主要是一些体积大,屏幕占比大,多是一些图片,视频,canvas等元素。这些元素加载完成的时间则可以近似的认为 FMP 的时间。
4.2.1.3 FMP算法设计
这样,我们就可以设计一个算法。
通过监听元素加载
根据可视区域面积,元素权重等关系,计算元素的得分
把子元素的得分之和和父元素得分做比对,取较大值,得到可视区域内得分最高的元素集合。
过滤分数超过平均分的元素,拿到更小范围的元素集合。
再对这些元素取最大的加载时间,这个时间则可以用来作为 FMP 的时间。
4.2.1.4 建设性能监控平台
完成了性能优化衡量指标后,就可以借助性能衡量指标来做一个性能监控平台,一个好的监控平台,往往有以下功能:
数据可视化:可以直观地看到各业务的实时数据大盘
慢加载追踪:提供慢页面列表和多维度数据
性能瓶颈定位:可以针对性的查看某个用户访问路径,查看用户各节点的性能瓶颈
性能报警:性能报告和线上报警
性能监控平台也是重要的一块内容,本篇文章主要介绍完整流程,后续再做介绍。
4.2.2 制定目标
完成性能度量体系后,就需要接入性能体系,对业务现状做数据分析,制定行动的目标。
以严选 C 端业务为例,制定了 FMP 1.2s 以内为达标标准,分成了三块业务:活动、用户、商城,根据现状数据和业务场景,制定了对应的目标。
4.2.3 性能优化实施
有了一个量化的目标后,就可以真正地进入到策略实施验证的阶段了。
4.2.3.1 团队组织和整体思路
先理一下思路:
我们可以基于衡量指标制作一个性能分析工具,专项的分析页面性能瓶颈
使用常规的性能优化策略,结合严选业务的优化策略
记录每次性能优化的数据变化
沉淀有效策略
在团队各业务中快速落地
思路可能过于理想,真实的落地中会遇到一些问题,根据之前的经验,觉得需要再加一条,建设一支强有力的队伍,来保证性能优化这个事项的落地。
大家对于平台/工具类的内容建设都比较热衷,但对于最终的落地都缺乏一定的执行力,因此采取一定的制度保障项目落地是非常必要的。
4.2.3.2 分析
性能优化的策略很多,如何很好地串联起这些策略点呢?可以先从这个问题开始:
从输入地址到页面显示经历了哪些过程?
简单的可以分为这几个阶段:
输入 URL 地址
缓存解析
域名分析
TCP 连接
服务器响应
浏览器渲染
从链路中,我们可以提取这几个关键词:缓存、TCP连接、服务器、响应体、渲染,性能优化将从这些关键词中产出关键策略。
页面链路:缓存
首先是缓存,缓存分为 HTTP 缓存、浏览器缓存、应用缓存、App缓存,对于前端,往往关注在前几个缓存,App 缓存这里介绍下,按需缓存就是之前经常反馈的 HTTP 缓存明明关闭了,为啥我的页面在 App 中还是不生效的罪魁祸首,以及 App 中非常有效的预缓存策略,可有效的提升页面性能。
页面链路:TCP连接(HTTP2.0)
下一步,TCP 连接,随着页面的复杂度提升,一个页面往往会有上百个请求,请求的头部信息往往都是一致的,头部开销偏大,升级到 HTTP2.0,则可以有效地节省这部分开销。
页面链路:服务器(CDN 服务器)
完成 TCP 连接后,进入服务器响应,对于服务器我们也有一定的优化点,对于静态资源,可以接入 CDN 服务器,可有效地提升网站的稳定性,大大地缩减资源加载时间。
页面链路:响应体(GZIP)
服务器返回响应体,响应体的内容未经压缩的话,体积往往比较大,采用 GZIP 压缩,可以大大的缩减文本类的资源体积,而对于图片资源则效果不大。
因为 GZIP 采用的是 LZ77 和哈夫曼算法,都是针对文本类的压缩算法,挺有意思,大家有兴趣的可以了解下。
页面链路:渲染(CRP 优化)
浏览器拿到响应体后,就会进入到渲染过程,解析 DOM 树,CSS 树,生成渲染树,计算节点,绘制页面。
这个过程叫做关键渲染路径,对于这类的优化可以统称为 CRP 优化。
CRP(关键渲染路径Critical Rendering Path)是浏览器从收到 HTML、CSS 和 Javascript 字节到对其进行必需的处理,从而将它们转化为渲染的像素这一过程。
CRP优化常规步骤:
关键路径分析和特性描述:关注三种可变因素:资源数、字节数、长度。
减少资源数。
缩小字节数。
缩短路径长度。
为减少资源数,缩小字节数,在工程化体系中,webpack 有很多的 loader 和插件,可以对 CSS,图片,JS 进行压缩和优化。
分析工具:webpack-bundle-analyzer
压缩CSS: css-loader?minimize(cssnano)、PurifyCSSPlugin
压缩图片:image-webpack-loader(imagemin-webpack-plugin)、webpack-spritesmith、url-loader(base64)
压缩JS:UglifyJS插件、ParallelUglifyPlugin
优化JS
DllPlugin 提升构建速度
splitChunks 拆包
按需加载 import (*)
页面渲染中,尽可能只加载首屏必要的元素可有效减少首屏渲染时间。
在 C 端业务中,图片资源占了页面资源大部分内容,在活动页面中,往往能占到 80% 的资源比例。
针对图片做懒加载,尺寸裁减,webp 等优化,则可以有效地减少加载的资源数,缩小字节数。对于图片懒加载可以同比类推,去实现页面中的模块懒加载,对于非首屏的内容,也没意义在初始渲染的时候进行加载渲染。
首屏渲染中,关注必要的数据请求接口,缩短关键路径长度。
比如页面中初始化的接口,可以采用极简的代码在页面 head 处执行,获取返回数据缓存到内存中,等到业务代码加载执行时可以直接获取缓存数据,理想情况下,就可以节省整个请求的耗时。
这类接口提前对于依赖初始化接口的页面做性能优化非常有效。
4.2.4 成果验收和复盘
实施一系列性能优化策略后,做好最后的成果验收和复盘。
性能优化记录:记录每次策略带来的性能优化,沉淀有效策略
性能周报:同步各业务负责人性能优化数据,以及待提升的页面列表
白皮书:体系化的性能优化流程与策略梳理,团队共享
5. 最佳实践
以上,较完整地介绍了前端性能优化的完整流程和体系。接下来介绍下网易严选业务中一些最佳实践。
5.1 整体架构
网易严选性能优化整体架构如下:
前端性能度量工具:APM 监控平台、性能衡量指标,对应的性能分析工具
前端性能增加基建:C 端页面可以借助端容器做接口预请求和资源预加载
页面接入性能 SDK,上报相关性能数据,针对页面进行性能瓶颈分析,专项优化页面性能和页面品质
5.2 资源预加载 Xcache
缓存是非常有效的一个优化点,但往往是第二次请求才有效,针对第一次请求也想应用到缓存,借助端容器就可以实现。
![Xcache](https://yanxuan.nosdn.127.net/16548311545376231.png?imageView&thumbnail=7 00x0)
资源预加载 Xcache 平台包含以下内容:
管理配置文件的后台。
配置下发服务。
客户端获取配置文件,拉取对应的资源列表,在 webview 发起资源请求时,判断是否命中缓存,命中则直接使用预缓存资源。
对应的行为数据统计。
5.3 静态页面动态化
在接入 Xcache 的过程中,遇到了一个问题。
电商行业有个明显的行业特性,就是页面的秒级切换。严选的营销页面部分是静态页面的,对于一个会场页面投放链接是固定的,比如存在预热期,正式期,返场期,页面内容的切换是通过定时的任务去刷新页面内容,虽然实现了秒级切换,但也意味着页面的 HTML 是不做缓存的,与接入 Xcache 矛盾。
基于现在的页面切换现状,最终采用了静态页面动态化改造的方案。
将一个静态的页面文件内容基于 AST 重新生成 bundlejs,这样对于预热期,正式期,返场期就会拿到三个 page bundlejs,再通过一个动态页面容器,结合条件路由配置信息,判断当前时间,加载不同时段的 page bundlejs,这样就能实现秒级切换,同时也可以将 HTML 和这些 bundlejs 提前缓存到 Xcache 中。
5.4 接口预请求Prefetch
接口预加载,类似于前面提到的接口提前,不同的是,接口预加载是通过容器代为发出,发送的更早,能更好的保证接口提前完成。
5.5 GIF懒加载
直接加载 GIF,约 2850.9kb VS 加载 GIF 的第一帧,约 76.9kb
在资源优化过程中,GIF 懒加载有了较好的数据提升,对于 GIF 图,可以只加载 GIF 的第一帧,等首屏渲染完成后,再完整加载 GIF 的资源,约缩小 97% 的首屏图片资源。
5.6 底部导航切换优化
在做了性能优化的基础上,也做了一些体验提升的内容:
比如底部导航切换优化,在切换页面时仍能保持底部导航常驻的状态,避免闪动带来的不适感。
5.7 页面过渡效果
主流的 SPA,MPA 框架主要逻辑都集中在 bundlejs 中,在逻辑载入执行之前页面仍然有一段白屏时间值得去优化。
可以在页面中插入一段 loading 逻辑,用于添加页面背景色,毛玻璃渐入效果,以及在弱网中的 loading 提示,进一步减少白屏焦虑。
再往前追述,还有 HTML 本身的加载耗时,这一段时间仍然值得去优化,如容器背景色或者 HTML 的缓存。
6. 结语
前面讲了较多的内容,或许你对性能优化流程又有点疑惑了,其实流程很简单,不妨再回顾下整个性能优化的流程。
我们在有意识的状态下,关注指标和数据,因此要先建设度量体系,其中包含衡量指标和对应的监控平台。
接入度量体系后,就可以看到当前的业务现状,制定相应的目标
接下来就是具体的性能优化策略实施
记住一个关键问题,从输入URL到页面展示经历了什么,抓住对应的关键词:缓存、tcp连接、服务器、响应体、渲染。基于每个关键词去扩展对应的优化手段。
在渲染中,记住 CRP 关键渲染路径优化 ,核心关注:资源数、字节数、路径长度的优化。
最后就是成果的验收和复盘
整个文章主要对全流程做了一下介绍,串联了各个优化手段,希望大家后续做一个完整的性能优化时,不再是各个分散的优化点,而是能想起上述的流程图,能完整的串联起各个优化点。