性能优化
前言
以前写过一篇性能优化的笔记前端性能优化小结,那时候算是列了一些优化的点,最近又读了几篇性能优化相关的文章,加上自己动手做了一些实践,相比之前有了更深一点的理解。
why?what?how?
如果要谈性能优化,我们应该先知道什么是性能优化,为什么要进行性能优化,再谈如何去做性能优化。
什么是性能优化
顾名思义,对于我们web开发来说,性能优化就是优化我们web应用的性能,而性能的体现应该在于应用的加载速度,渲染速度,交互响应速度这些感性指标。
性能优化指标
什么样的应用性能好呢?用户可以通过直观感受来描述一个网页的快慢,响应速度。但是对于开发者来说,无疑我们需要一些可以定量的指标去测量网页的性能,优化的时候也可以测量出优化到底是否有提升及提升多少
- FCP(first contentful paint)首次内容渲染时间
- FP(first paint)首次渲染
- FMP(first meaning paint)首次有效渲染(主要内容渲染时间,可通过最大渲染DIFF时间得到)(指首屏)
- LCP(largest contentful paint)(最大元素渲染时间)
- TTI(time to interactive)可交互时间(JS主线程已经流畅)
- DCL(domcontentloaded)DOM解析完成时间
- L(onloaded)DOM解析完成且所有资源标签完成加载时间
- Speed Index 页面渲染速度(可见内容的平均可见时间,可用于描述页面可见速度)
- FID(first input delay)首次输入延迟(指用户首次输入的响应时间,受到用户输入时间影响)
- FCI(first cpu idle)CPU首次空闲时间(此时可以响应用户输入,但是和IIT相比,此时无法持续响应,可能只是某个间隙空闲)
- TTFB(time to first byte)浏览器接收首个字节的时间
以上罗列了很多可以测量页面性能的指标,在我们优化性能的时间可以通过以上指标来测量优化效果及评估优化方案。对于单个页面来说,我们应该从实际出发挑选合适的监测指标而不用一股脑全部优化,它们之间可能是相互影响的,当优化其中一个指标的时间实际上可能拖慢其余指标
。
为什么要进行性能优化
性能优化是为了提高用户体验,而提高用户体验是为了让用户能够实现使用目的及更好的留存。
性能的影响因素
回到用户对于性能的感性指标,页面加载速度,渲染速度,交互响应速度,对于这几个指标有两个关键的影响因素
- 网络方面
- 代码层面
从不同的方面入手可以有不同的优化方式
- 网络方面
- 减小资源体积
- 优化网络及连接
- 缓存
- 代码层面
- 从代码的执行原理出发进行优化(比如减少css嵌套,JS变量缓存)
- 控制代码执行顺序(预加载,懒加载)
- 代码优化(减少重排重绘,减少不必要的DOM操作)
在不同过程中进行性能优化
前面我们从网络及代码方面,但可能不方便我们梳理和记忆,所以我们接下来从构建到页面渲染的流程进行优化
- 项目构建(比如使用webpack打包项目时)
- 资源传输
- 关键渲染路径
- 代码执行顺序
- 代码质量
- 其它
1.项目构建
在项目构建的时候我们可以做的优化有
- 压缩JS/CSS/Image(一般我们的cli都有默认配置)(压缩CSS的时候可以去重)
- 对于CSS可以使用PurgeCSSPlugin来做到treeShaking
- 小图标的base64(减少请求数量)(为什么说小图标呢,因为base64编码实际比png格式大)
- treeShaking(减少资源传输体积并减少JS编译时间)(webpack5已经默认
- treeShakign)(需要项目使用ESModule)
- 分包(splitChunk)(将大文件分割成比较小的bundle可以帮助我们优化缓存,也可以减少不必要的资源传输)
2.资源传输
针对资源传输我们可以做以下优化
- 启用压缩(常用的有GZIP,BR等)压缩在webpack处做)
- CDN加速(webpack中可以配置CDN地址统一替换)
- 合理利用浏览器缓存(一般webpack打包的JS/CSS配置了hash可以使用强缓存)
- 资源放在不同域名(基于浏览器对相同域名的6个TCP连接限制)
- http2(通过将请求切割为流,使用流标识组合来共用TCP连接)
- 雪碧图(减少请求数量)
3.关键渲染路径
关键渲染路径是指HTML解析到页面呈现的过程,包括DOM解析,CSSOM解析,渲染树合成,布局及页面绘制,在这个过程中我们可以做以下优化
- 减少无用标签(减少标签可以加快DOM解析)
- 减少重复样式(webpack压缩的可以将重复样式去除)
- 减少CSS层级(基于css从右向左的寻找可以降低解析时间)
- 明确元素大小,比如图片宽高(可以避免不必要的重排/抖动)
- css放在head(可以减少css覆盖导致的重排重绘)
- Js的async/defer属性(去除不必要的阻塞)
4.代码执行顺序
- 预加载(包括图片,接口)(降低交互反馈时间)
- 懒加载(节约网络资源,可以提升当次任务体验,如首屏渲染)
- prefetch/preload(JS子包的预加载及执行)
- 动态import(prefetch/preload的实现)
- 动态polyfill(嗅探是否需要polyfill)
- 服务端渲染(本质是将代码放在服务端执行)
5.代码质量
- 计算结果的缓存(以空间换时间,例如递归算法的缓存)
- 数据缓存
- 变量寻找的缓存(例如a.b.c,可以降低寻址的时间)
- 避免频繁的DOM操作
- 减少重排重绘/抖动(例如节点的统一替换)(控制渲染时序)
- 防抖节流(防抖一般用于关键字搜索,节流多用于scroll监听)
- 开启动画3D硬件加速(携带3D的transform,opacity等动画)
- 选择适合的图片格式(例如webp可以节约30%+的大小)
- 利用CDN服务的参数优化图片请求(例如定义合适尺寸的宽高来裁剪图片)
- css的animation/transition代码JS动画
6.其它
- 骨架屏(提前告知用户关注点,降低白屏时间)
- requestAnimationFrame(可以准确匹配渲染,避免丢帧)
- webworker(JS多线程)
- requestIdleCallback(利用JS引擎的空闲时间)
- IntersectionObserver(监测元素可见)
- MutationObserver(监测DOM更新)
性能测量工具
- Chrome DevTools(network及performance面板)
- lighthouse
- performance API
后话
实际性能优化是基于影响性能的角度考虑,从开发到渲染的不同流程中对其进行优化提升,其实现基于无数的小细节。对于不同的页面来说,我们重点关注的指标可能有所不同,所以性能优化基于实际情况,通过各种手段的结合来达到适合当前的方案。
还有一个就是不同的优化细节产生的效益其实是不同的,比如开启服务端压缩可以做到减少50%+的资源体积,对于资源传输的收益是非常明显的。而控制css的嵌套层级,也许仅仅能减少几毫秒的解析时间,收益非常小。所以还是那句话,应该有针对性的去优化性能,制定方案,而不是一股脑的我全都要。