一、浏览器的渲染原理。
我们想要在页面渲染上做性能优化,首先要了解浏览底层机制的渲染原理。
浏览器渲染步骤:
(1)css资源还没有请求回来之前,先生成DOM树(DOM的层级关系/节点关系)
(2)当有的css资源请求回来之后,浏览器按照css的导入顺序,依次进行渲染,最后生成CSSOM树
(3)把DOM树和CSSOM树结合在一起,生成有样式有结构的RENDER-TREE渲染树
(4)浏览器按照渲染树,在页面中进行渲染和解析
=> 1) 计算`元素在设备视口中的大小和位置布局(Layout)或重排/回流
=> 2)根据渲染树以及回流中得到的集合信息,得到节点的绝对像素=>重绘/重排(painting)
二、图解构建DOM树、CSSOM树、RENDER树
转换:把进制数据转换为代码字符串
词法分析:依托w3c规范生成对应的节点
DOM/CSSOM构建:根据节点生层对应的树
[构建DOM树]
【CSSOM树】
【Render-Tree渲染树】
总结步骤:
处理 HTML 标记,构建 DOM 树 处理 CSS 标记,构建 CSSOM 树 将 DOM 树和 CSSOM 树融合成渲染树
根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流 => 布局(Layout)或
重排(reflow) 根据渲染树以及回流得到的几何信息,得到节点的绝对像素 => 绘制(painting)或栅格化(rasterizing)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="xxxx.css">
<title>Document</title>
<style>
.red{
color:red;
background: lightblue;
}
</style>
</head>
<body>
<div class="box">
<span>
<a href="#"></a>
</span>
</div>
</body>
</html>
<script src="xxx.js"></script>
看下上边的一个常规html页面,页面中引入样式一般使用link、@import、style方式引入:
(这里的@import不包括vue或react中的@import样式引入,因为vue和react等都会基于webpack打包成一个css样式,再在入口文件中以link的方式引入)
1、遇到link浏览器会派一个新的线程(http线程)去加载资源文件,与此同时GUI渲染线程会继续向下渲染代码…无论css是否请求回来,代码继续渲染
2、遇到的是@import,GUI渲染进程会停止渲染,去服务器加载资源文件,资源文件没有返回前是不会继续渲染的@import会阻碍页面的渲染,项目中尽可能少用
3、遇到style,GUI直接渲染
进程:每一个程序都是一个进程,浏览器打开一个页面就开辟一个进程
线程:一个页面渲染,会有许多的线程做不同的事情,http线程去请求资源文件
GUI线程负责渲染页面
比如一个饭店就相当于一个进程,服务员就相当于线程。一个进程可以有多个线程。
正常情况下JS也会阻碍GUI渲染
所以我们在开发的过程中:
1)JS放在页面尾部,就是为了确保DOM树生成完才会去加载JS
2)使用defer和async异步管控JS请求
三、页面渲染可以做以下几个方面的性能优化:
1、标签语义化和避免深层次嵌套
(html的层级尽可能少,因为层级多了影响布局(layout)计算时间)
2、css层级不宜过多,CSS选择器渲染是从右到左
(如.box ul li span a{color:red}这种方式层级过多,影响页面渲染,使用sass/less深层嵌套是影响性能的)
3、尽早尽快地把CSS下载到客户端
(充分利用HTTP多请求并发机制,浏览器http请求的并发数在6-7个)
- style(如果页面样式较少大概100-200行,直接使用style引入样式)
- link(link引入样式放在顶部)
- @import(@import引入样式放在页面底部)
4、避免阻塞的JS加载
(JS放在页面底部,合理使用defer、async)
5、避免DOM的重排/回流:
重绘:元素样式的改变(但宽高、大小、位置不变)
重排(回流):元素大小或位置发生变化(当页面布局和几何信息发生变化),触发了重新布局,导致渲染树重新布局和渲染。
注意:重排一定会触发重绘,而重绘不一定会重排
优化方案:
1)放弃传统操作DOM的时代,基于vue/react开始数据影响视图模式;
2)分离读写操作(现在的浏览器都有渲染队列机制)
3)样式集中改变
4)缓存布局信息
5)元素批量修改
6)动画效果应用到position属性为absolute或fixed的元素上(脱离文档流)
7)css硬件加速(GPU加速)
8)牺牲平滑度换取速度
9)避免table布局和使用css的javascript表达式
(如width:calc(100% - 20px)这个就属于css的js表达式)