前端性能优化
前端性能优化
性能优化分为两个部分
一个是时间上的优化也就是减少耗时,缩短用户等待时间,另一个是空间上的优化,也就是减少资源对用户内存的占用
时间方面
时间方面又分为 网络请求优化,首屏加载优化,渲染过程优化,计算逻辑优化
网络请求优化
网络请求优化的目标在于减少资源请求数量,降低加载耗时,参考 http 请求过程,可以考虑这几点优化
请求链路
包含 DNS 查询、部署 CDN 节点、缓存等
请求链路优化主要是通过 **DNS 缓存(通过 dns-prefetch 对同源请求进行 DNS 预查询,之后直接使用缓存即可),CDN 加速(通过 CDN 引入第三方依赖,客户端使用时会去最近的 CDN 服务器引入,即提升了请求速度又保证代码体积最小化),HTTP **缓存,后台缓存等,像是 service worker 、PWA 等缓存方式
使用 **HTTP2/3 **等提升请求速度
对请求进行优化,比如多个请求合并,减少通信次数
对请求进行域名拆分,提升请求并发数量
数据大小
包含代码体积、图片资源等
首屏加载优化
- 主要就是把页面尽快地展示给用户,减少页面白屏时间
- 以及将用户可操作的时间尽量提前,避免用户无法操作的卡顿体验
首屏渲染包括了首屏内容的加载和渲染两个过程
首屏内容加载
使用骨架屏进行预渲染
对页面进行分片/分屏加载,将页面可见/可交互时间提前
优化资源加载的顺序和粒度,仅加载需要的资源,通过异步加载方式加载剩余资源
使用服务端直出渲染,减少页面二次请求的渲染和耗时
首屏内容渲染
首屏内容渲染过程,就是浏览器渲染 HTML 的过程比如:
将 css 放在 head 里,用来避免浏览器渲染的重复计算,因为 head 不再渲染树中
减少 DOM 数量,减少浏览器渲染过程中的计算耗时
使用骨架屏预渲染
分屏加载、将页面可见/可交互时间提前
资源懒加载
虚拟滚动
渲染过程优化
渲染过程的优化要先明确渲染过程的定义和触发的时机,渲染过程其实就是用户交互后会触发页面的二次渲染,浏览器主线程会执行交互事件内的逻辑,期间浏览器遇到 dom 操作就会立刻重绘页面中对应的节点,直到执行完这个交互事件,算一次渲染过程
这个渲染过程花费的时间越少,用户交互延迟时间就越短,渲染过程优化首先就是合并、减少 DOM 的操作
其次交互逻辑中可能会包含请求,那么就可以在用户交互之前进行资源空闲加载,减少渲染过程中数据请求的开销
如果这请求返回的数据是需要以表格形式大量渲染的,那么可以通过 canvas 来进行数据绘制,避免大量创建 dom 节点,因为后续如果对这些表格的 dom 节点进行批量操作的话,浏览器重绘的过程中不仅是内容渲染,还有 style 样式的重绘,在确保背景不会变的情况下,类似背景重绘的性能开销是非常没必要的
其次如果数据过多还可以采取分页渲染的方式,降低一次性渲染负担
非实时的分页渲染的数据其实也可以提前进行 canvas 离屏渲染,在看不见的地方进行绘制,随后缓存绘制结果图像数据,当需要使用时可以直接在主画布中替换,可以减少后续分页绘制的时间和次数
需要对 canvas 表格数据放大操作时,不需要对 canvas 重新绘制缩放,虽然 canvas 会调用 gpu 加速渲染,但也要合理使用,像缩放这种轻量需求可以直接使用 css transform 对 canvas 元素本体进行操作
还有要注意的是二次交互的逻辑计算不能太大,因为浏览器的帧率限制是一次 16.67ms,交互的每次触发需要在这时间内完成 JS 逻辑计算和页面重绘,否则就会造成页面交互延迟,也就是卡顿,可以针对进行计算逻辑优化,如果在这时间内完成了逻辑和重绘或者用户不执行交互的空闲时间内,浏览器会可以执行一个叫 requestIdleCallback api,这个 api 就是为了空闲情况下做额外操作而设计的,有点类似 settimeout 只执行一次时机不同
将 DOM 操作合并,减少操作 DOM 次数
使用资源空闲加载,提升空闲时间的资源利用率
使用 canvas 绘制数据 ,比如 echarts
数据多时使用分页渲染
使用离屏渲染;在看不到的地方提前渲染,比如 canvas 离屏渲染
合理使用 GPU 渲染,比如通过 css transform 来代替 canvas 缩放绘制
requestAnimationFrame 和 requestIdleCallback 卡浏览器帧率进行回调执行,
浏览器的帧率限制是 16.6ms 更新一次,假设 js 执行渲染过程超过了 16.6 ms 就会延迟响应了,但如果在时间内做完了渲染过程就可以执行 requestIdleCallback,或者是空闲时执行,就可以在不影响帧率的前提下来执行额外的操作
计算逻辑优化
首先就是将 JS 大任务拆解,结合异步任务使用来避免长时间的计算导致的页面卡顿,分离出的数据处理操作可以集中起来采用 web worker 创建的独立线程进行计算,让主线程只负责主逻辑计算和页面渲染,大幅降低页面阻塞的可能性,注意副作用函数的清除,避免异步任务创建的线程过多导致性能瓶颈,其次就是将计算结果缓存,减少计算次数,例如 vue 的 computed api ,以及使用一些迭代器方法时尽量使用有算法优化的,比如 lodash 的 foreach 等,如果想提高逻辑的运行效率,可以通过 WebAssembly api 编写 C++ 等语言,最终都会被编译成浏览器可执行的语言,计算效率会更高,再有就是算法和存储结构上的优化了
将 JS 大任务拆解,结合异步任务,避免出现长时间计算导致页面延迟
将数据处理的操作集中起来,通过** web worker 创建的线程 来执行相应的方法,主线程只负责 UI 渲染和交互逻辑处理,可以大幅提升主线程的渲染效率
注意副作用的清除,避免异步创建的线程过多导致性能瓶颈**
通过计算结果缓存,减少运算次数,例如 vue 的 computed
在使用防抖、节流时通过** lodash 调用,lodash 内部有通过算法进行优化,包括使用迭代器遍历大量数据的时候,尽量使用 lodash 的 forEach 方法,可以把遍历性能损耗尽量降到最低
通过使用运行效率更高的方式来减少耗时,比如 Webassembly
通过更优的算法或者储存结构**,提升计算效率
对作用域的控制
1 |
|
空间方面
包括 cdn 、代码和资源体积压缩等
合理的使用缓存,杜绝滥用用户缓存,周期性的清理浏览器缓存,减少资源占用