关于页面渲染,下面列出了很多相关的技术文章,这里再做一下总结和补充:
浏览器渲染Rendering那些事:repaint、reflow/relayout、restyle
页面渲染过程
1. 请求页面文档
2. 解析页面文档
2.1) 解析HTML,构建DOM树;
2.2) 遇到内联JS/CSS,阻塞页面文档的解析,并立即解析JS/CSS,JS/CSS解析完成后,再继续往下解析页面文档;
2.3) 遇到外部脚本,立即下载脚本,若下载的脚本为<script>或<link>,则会阻塞页面文档的解析,等脚本下载并解析完成后,再继续往下解析页面文档;
上面的解析流程还包括有下面几个场景:
i) 2.2和2.3阻塞页面文档解析,只是阻塞了DOM树的构建,并不阻塞页面预解析,预解析页面文档,会解析出页面其它的外部资源,然后并行下载这些外部资源,单次并行下载的外部资源数量与浏览器有关;
ii) IE浏览器,当<script>标签包含defer属性时,脚本的下载不会阻塞页面其它任务,脚本下载完成后不会立即执行,等到页面解析完成后,再将全部以defer方式下载的脚本按发起请求的顺序执行;
iii) 现代浏览器(chrome,firfox,IE10+等),当<script>标签包含async属性时,脚本的下载不会阻塞页面其它任务,脚本下载完成会立即执行;
3. 构建render树
render树的每个节点为一个渲染对象,类似CSS的盒子概念,与DOM树的节点基本对应
4. 布局render树
4.1) 设置render树节点的大小位置等属性,根据样式属性构建渲染层(绝对定位/透明/transform/canvas/等);
4.2) 布局分同步布局(字体变化,窗口resize事件)和异步布局,同步布局会立即执行,异步布局则会将任务提交至一个队列,等需要获取节点样式属性时再执行;
5. 绘制render树
5.1) 设置render树节点的颜色背景等属性;
5.2) 将渲染层栅格化,并将纹理上传到GPU,然后将图层合成显示;
在整个页面的渲染过程,上面的步骤并不是只执行一次,通常是解析8K文档内容后,会执行一轮上面步骤,用户可以看到已渲染完成的页面内容,然后浏览器会继续开始下一轮渲染。
页面重排重绘
对应渲染完成的页面,当样式变化时,会重新计算样式,布局元素,绘制元素;页面元素大小位置等变化会重新布局元素,绘制元素;页面元素颜色背景等变化会重新绘制元素。布局元素时,同时会布局他的子元素,若还影响到了其它元素的大小位置等属性的变化,还会导致这些元素也重新布局。重新布局叫重排,重新绘制叫重绘。
重排重绘的优化方案:
1) DOM操作读写分离:对于页面元素的重排和重绘都会,若属于4.2提到的异步布局的范畴,则重排重绘不会立即执行,等到到读取页面元素的样式属性时,才会触发。因此DOM操作的读写分离可以减少重排重绘次数;
2) 先操作DocumentFragment,后插入DOM树;
3) 先复制DOM节点,修改后再替换到DOM树;
4) 通过修改一次style或class来修改DOM属性;
5) 通过requestAnimationFrame操作DOM属性:requestAnimationFrame会将参数中的回调函数延迟到下一帧渲染时执行;
6) CSS3动画替换绝对定位的位移:CSS3动画中元素的变化,都是在新图层在GPU里执行,而不需要重新布局。