最近重新整理 慕课(前端性能优化企业级解决方案 6大角度+大厂视野)。
所以打算从 0 - 1 记录下来如何使用性能优化工具、如何分析前端性能优化,以及主流的前端性能优化方案。
全程采用案例分析的方式,一步一步敲开前端性能分析和优化的大门。
这是 前端性能优化企业级解决方案 的第二篇 渲染优化、代码优化、资源优化。
渲染优化
现代浏览器渲染原理-关键渲染路径
什么是关键渲染路径(critical rendering path)?
描述了渲染从触发到绘制的整个过程。
JavaScript
触发我们的视觉变化(
JS、CSS等方式)。
- Style
浏览器重新对样式进行计算。
- Layout
布局。
- Paint
真正开始 绘制页面。文字、颜色、图片、阴影等。
- Composite
复合。
如何查看浏览器的渲染流程?
Performance 分析渲染过程。
浏览器拿到服务器的资源,都做了什么?
浏览器通过解释器把文本翻译能理解的数据结构。
- html 是怎么被转换的?
构建DOM对象。
html -> DOM tree。
- css 是怎么被转换的?
构建CSSDOM对象。
css -> CSS tree。
- 浏览器构建
渲染树
DOM + CSSDOM => Render Tree。合并把
真正需要显示的内容留下,不需要显示的内容去掉,浏览器知道渲染的具体内容和位置。
布局(layout)和绘制(paint)
layout(回流)
布局计算每个节点精确的位置和大小 - 盒模型。
- 触发
layout的操作ss
添加/删除 元素
操作styles
display:none
offsetLeft,scrollTop,clientWidth
移动元素位置
修改浏览器大小、字体大小
避免layout thrashing 布局抖动
避免回流,读写分离。
- 使用FastDOM
paint(绘制)
绘制是像素化每个节点的过程。
- 注意
GPU加速的动画,直接走Composite(不会经过layout、paint)的过程。
复合线程(compositor thread)与图层(layers)
- 复合线程做了什么?
将
页面拆分图层进行绘制,再进行复合。
- 默认情况
默认情况下是由浏览器来决定的,分析元素与元素之间是否有相互的影响。
如果某些元素对其他元素造成的影响非常的多,就会被提取成一个单独的图层。
- 主动提取
把一些元素主动提取成一个图层。
.card {
whil-change: 'transform'; // transform、opaicty
}
- 利用DevTools 了解网页的图层拆分情况
- 哪些样式
仅影响复合
极大的提高关键渲染路径的效率。
对案例图层,进行性能分析。
开发者工具 -》 Performance -》 录制 -》 触发 动画 -》 关闭录制
transform、opacity等样式,仅仅影响复合,不会触发layout(回流)和paint(重绘)。
高频事件防抖
- 一帧的生命周期
事件 -> JS -> 一帧开始 -> rAF -> layout -> Paint
rAF浏览器调度的,尽量的在每一次绘制之前触发rAF。以满足能达到60FPS 这个效果。
案例:---
React时间调度实现
requestIdleCallback 的兼容性问题,react 通过 rAF模拟实现了rIC。
代码优化
快来看看怎样写出真正高性能的代码。
JS开销和如何缩短解析时间
JS 的开销在哪里?
解析、编译的时间太长。
加载 -》 解析&编译(2s) -》执行(1.5s)。
解决方案:
-
Code splitting 代码拆分,按需加载
-
Tree shaking 代码减重
-
避免长任务
-
避免超过1KB的行间脚本
-
只用rAF 和 rIC 进行时间调度
-
渐进式启动
可见不可交互 VS 最小可交互资源集。
先加载首屏,剩下资源
延时加载。
配合V8有效优化代码
V8 编译原理?
V8 引擎拿到JS源代码 -》 解析 -》 AST -》 解释器 -》优化 -》字节码 -》 机器码。
编译过程会进行优化。
运行时可能发生发优化。
V8优化机制:
- 脚本流
当下载的脚本大于30kb 的时候,会单独开一个线程,对着30KB的内容先进行解析。
当整个脚本都加载完成之后,再进行解析的时候,效率就提高了,因为前面的30KB已经解析了。
把所有解析的内容进行合并,就可以进行执行了。
- 字节码缓存 s
重复使用的片段,缓存起来。
- 懒解析
是对于函数而言的。
先不去解析函数内容,等
真正要用的时候再去解析。
函数优化
函数的解析方式。
- 懒解析
先不去解析函数内容,等真正要用的时候再去解析。
- 饥饿解析
声明的时候就去解析。
语法:
函数声明包上 ()。
const add = ((a, b) => a + b)
对象优化
对象优化可以做哪些?
- 以
相同顺序初始化对象成员,避免隐藏类型的调整
编译器在解析的时候,会根据自己的推断给这些变量赋予 隐藏类型(21种)。
- 实例化后避免添加新属性
- 尽量使用Array 代替 array-like 对象
- 避免读取超过数组的长度
- 避免元素类型转换
HTML优化
-
减少iframes 使用
-
压缩空白符
-
避免节点深层级嵌套
-
避免table布局
-
删除注释
-
CSS& JavaScript 尽量外链
-
删除元素默认属性
CSS对性能的影响
利用DevTools测量样式计算开销?
优化:
-
降低CSS对渲染的阻塞。
-
利用GPU进行完成动画。
-
使用
contain属性。
contain: layout
盒子里的子元素和盒子外的子元素,没有任何的关系(里面怎么变化不会影响外面,外面怎么变化也不会影响盒子里面)。
- 使用
font-display属性。
文字更早的显示在页面,解决文字闪动的问题。
资源优化
资源的压缩与合并
为什么要压缩、合并?
见效最明显的优化方法。
减少http请求数量。
减少请求资源的大小。
html的压缩
html-minifier。
CSS的压缩
clean-css等npm 工具。
- JS 压缩与混淆
Webpack对JS在构建时压缩。
- CSS JS文件合并
图片格式优化
图片优化的方案?
-
选择正确的图片格式
-
合适的图片大小
-
适配不同屏幕的尺寸
-
压缩
-
加载优先级
-
懒加载
-
利用工具
图片格式比较
- JPEG/JPG
- JPEG/JPG 的优点
有损压缩,压缩比高,色彩好。
- 使用场景
大图。
- 缺点
图片的
纹理、边缘会有锯齿感和模糊。
- PNG
- 优点
透明图。
- 使用场景
小图,图标、logo。
- 缺点
体积大
- WEBP
- 优点
跟PNG达到相同的质量,体积更小
- 缺点
浏览器兼容性。
图片加载优化
图片的懒加载
- 原生的图片懒加载方案
img标签上添加loading=lazy需要浏览器支持,自定义和可扩展不太好。
- 第三方图片懒加载方案
verlok/lazyload
yall.js
Blazy
渐进式图片
- Baseline JPEG
基线格式的JPEG,行扫描的方式显示图片。
- Progressive JPEG
渐进式格式的JPEG,从模糊到清晰的显示图片。
渐进式图片的优缺点?
始终可以让用户看到图的全貌,慢慢从模糊到清晰的过程。
渐进式图片的解决方案?
-
progressive-imgage
-
ImageMagick
-
libjpeg
-
jpegtran
-
jpeg-recompress
-
imagemin
响应式图片
适配不同的屏幕尺寸,下载对应的图片资源。
- 响应式加载
Srcset、Sizes属性的使用。
- picture 元素
字体优化
什么是FOIT和FOUT?
字体未下载完成时,浏览器隐藏或自动降级,导致字体闪烁。
- Flash Of Invisible Text
文字
从看不到到看到。
- Flash Of Unstyled Text
文字从
没有样式到看到样式。
font-display
font-display: xxx 控制字体加载的行为。
支持以下属性值,表现方式如下:
INVISIBLE 不可见。
FALLBACK 默认字体。
WEBFONT 网站字体。
AJAX + Base64
解决兼容性问题?
使用Base64 将字体编码,保存到css / js 中。
- 缺点
缓存问题:缓存依赖于css/js ,缓存不可控。