前端性能优化(渲染篇)
浏览器的运行机制
主要功能模块 (HTML 解释器、CSS 解释器、图层布局计算模块、视图绘制模块与JavaScript 引擎)
-
HTML 解释器:将 HTML 文档经过词法分析输出 DOM 树。
-
CSS 解释器:解析 CSS 文档, 生成样式规则。
-
图层布局计算模块:布局计算每个对象的精确位置和大小。
-
视图绘制模块:进行具体节点的图像绘制,将像素渲染到屏幕上。
-
JavaScript 引擎:编译执行 Javascript 代码。
优化建议
- 因为CSS 选择符是从右到左进行匹配的
// 不建议使用,浏览器会遍历页面上每个li元素,并且每次都要去确认这个li元素的父元素id是不是myList
#myList li{}
- CSS与JS的加载顺序优化
-
CSS 是阻塞渲染的资源。需要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间。比如css放在head标签中。
-
JS的三种加载方式(当脚本与DOM元素和其它脚本之间的依赖关系不强时,选用async;当脚本依赖于DOM元素和其它脚本的执行结果时,我们会选用 defer)
// 正常模式:这种情况下 JS 会阻塞浏览器,浏览器必须等待 index.js 加载和执行完毕才能去做其它事情。
<script src="index.js"></script>
// async 模式:async 模式下,JS 不会阻塞浏览器做任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会立即执行。
<script async src="index.js"></script>
// defer 模式:defer 模式下,JS 的加载是异步的,执行是被推迟的。等整个文档解析完成、DOMContentLoaded 事件即将被触发时,被标记了 defer 的 JS 文件才会开始依次执行。
<script defer src="index.js"></script>
减少 回流(Reflow)与重绘(Repaint)
-
回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流。
-
重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过回流环节)。这个过程叫做重绘。
- 将多次修改保存起来,一次性修改
// 优化前
const el = document.getElementById('el');
for (let i = 0; i < 10; i++) {
el.style.top = el.offsetTop + 10 + "px";
el.style.left = el.offsetLeft + 10 + "px";
}
// 优化后
const el = document.getElementById('el');
let offLeft = el.offsetLeft, offTop = el.offsetTop;
for(let i=0;i<10;i++) {
offLeft += 10;
offTop += 10;
}
el.style.left = offLeft + "px";
el.style.top = offTop + "px";
- 避免逐条改变样式,使用类名去合并样式
// 优化前
const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
// 优化后
const container = document.getElementById('container')
container.classList.add('basic_style')
- 将 DOM “离线”
- 一旦我们给元素设置 display: none,将其从页面上“拿掉”,那么我们的后续操作,将无法触发回流与重绘——这个将元素“拿掉”的操作,就叫做 DOM 离线化。
// 优化前
const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
// 优化后
let container = document.getElementById('container')
container.style.display = 'none'
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
container.style.display = 'block'