reflow(回流、重排)
repaint(重绘)
浏览器的渲染机制
- 浏览器采用流式布局模型
- 首先浏览器会将html解析成dom 树,css 解析成 cssom,把二者结合起来,产生一个 render tree
浏览器的渲染原理
浏览器是多线程的,至少有三个常驻线程:JavaScript引擎线程、GUI渲染线程、浏览器事件触发线程
js是单线程的,js在浏览器中可以是多线程的。
JavaScript引擎线程:
单线程的,解析执行js代码的
GUI渲染线程:
渲染页面的,和js 引擎是互斥的,挂起
这就是js阻塞页面加载
浏览器事件触发线程:
当一个事件被触发的时候,这个线程会把事件添加到任务队列的队尾,等待 js引擎的处理
浏览器
是一个应用软件
内核:
trident:ie
webkit 内核:chrome、safari
gecko:firefox
浏览器渲染的主要步骤:
将获取的 HTML 文档解析成 dom 树
在解析开始的时候,浏览器会启用另外一个线程来下载css文件、静态资源等文件,如果 遇到script 标签就停止解析,转而去执行 js 和 加载 js文件,完成之后,再去继续解析 css 等资源
css 下载完成之后,对css 进行解析,得到 cssom 树
- 当 dom 树和 cssom 树都构建完成之后,会根据他们生成一颗 render tree,渲染树
布局 layout
浏览器采用流式处理的办法对render tree 上的所有节点进行遍历,计算它们在屏幕上的布置
绘制,遍历 render tree ,将其绘制在屏幕上
渲染的具体过程:
将获取的 HTML 文档解析成 dom 树
display:none 元素也会在 dom 树中 注释也会在 dom 树中 script 标签也会在 dom 树中
cssom 树
css 解析可以与dom解析同时进行 css 的解析 与 script 执行互斥 在webkit内核中进行了优化,只有在js访问css的时候才会发生互斥
render tree
这棵树是将来要显示到页面上的内容 遍历 dom 树节点,为每一个节点加上合适的css样式 display:none 的元素不会显示在渲染树中 visibility:hidden 会显示在渲染树中 每个节点的宽度和高度、背景颜色等信息会被确定
布局layout
遍历渲染树,计算上面每一个节点的位置信息,将其渲染到页面上的正确位置 float、absolute、fixed 脱离文档流,指的就是脱离 渲染树 重排发生在这一阶段
绘制
在绘制阶段,浏览器会遍历渲染树,调用渲染器的paint() 方法在屏幕上显示内容 GUI线程完成这一工作
阻塞渲染
当浏览器对HTML页面进行解析的时候,如果遇到了 script 标签 内部脚本就等到脚本执行结束再去继续解析,如果是外部脚本,要等到下载完成 在解析页面。
如果脚本还操作了 css,此时 cssom 还没有构建完成,会停止js 的加载,直到 cssom 树构建完成 因为CSSOM树中包含了各个节点的样式信息,CSSOM树没有加载完成就没有办法进入下一阶段,在这之前用户看到的页面一直是空白的。这也是为什么要把CSS代码放在标签中的原因。
为什么遇到script标签就是要停下来HTML文档的解析,因为js脚本中可能包含有对DOM或者CSSOM的操作,脚本解析会将脚本中改变DOM和CSS的地方分别解析出来,追加到DOM树和CSSOM规则树上。
回流
回流通俗的来讲就是页面的布局发生了变化,浏览器需要重新的计算各个节点的位置,大小等信息了,浏览器会从root frame递归向下计算。这个回去重新计算的过程就是回流。回流是无法避免的,并且也不知道页面具体哪部分会受到影响,因为他们是相互依存,互相影响的关系。
哪些操作会引起回流?
网页初始化的时候 DOM操作(元素添加、删除、修改、元素顺序变化) 内容变化 添加删除样式表 窗口缩放 伪类激活,例如 hover 悬停 JS 获取 Layout 属性值(如:offsetLeft、scrollTop、getComputedStyle 等)也会引起回流。因为浏览器需要通过回流计算最新值
回流必定引起重绘,但是重绘不一定引起回流
重绘
重绘是指一个元素的外观被改变了,背景颜色、文字颜色、边框颜色等。就会引起浏览器对某一部分的重画,但是并不会引起页面布局的改变。
如何避免回流和重绘
- 可以将需要多次修改的DOM元素设置display:none,操作完再显示。(因为隐藏元素不在render树内,因此修改隐藏元素不会触发回流重绘)
- 用transform做形变和位移可以减少reflow
- 操作完成后再显示 需要创建多个 DOM 节点时,使用 DocumentFragment 创建完后一次性的加入 document
- 缓存 Layout 属性值,如:var left = elem.offsetLeft; 这样,多次使用 left 只产生一次回流
- 尽量避免用 table 布局(table 元素一旦触发回流就会导致 table 里所有的其它元素回流)
浏览器优化的建议
- 样式文件应当在head标签中,而脚本文件在body结束前,这样可以防止阻塞的方式
- 简化并优化CSS选择器,尽量将嵌套层减少到最小。
- 不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式
- 尽量用transform来做形变和位移
- 先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。
- 只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。