想要了解重绘和回流,就要了解浏览器的工作原理
浏览器的组成
-
用户界面- 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分
-
浏览器引擎- 用来查询及操作渲染引擎的接口
-
渲染引擎(浏览器内核)- 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来
-
网络- 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作
-
UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口
-
JS解释器- 用来解释执行JS代码
-
数据存储- 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了Storage技术,这是一种轻量级完整的客户端存储技术
主流的渲染引擎
浏览器的渲染引擎也叫排版引擎,或者是浏览器内核
主流的 渲染引擎 有
- Chrome浏览器: Blink引擎(WebKit的一个分支)。
- Safari浏览器: WebKit引擎,windows版本2008年3月18日推出正式版,但苹果已于2012年7月25日停止开发Windows版的Safari。
- FireFox浏览器: Gecko引擎。
- Opera浏览器: Blink引擎(早期版使用Presto引擎)。
- Internet Explorer浏览器: Trident引擎 。
- Microsoft Edge浏览器: EdgeHTML引擎(Trident的一个分支)。
渲染引擎工作原理
渲染引擎解析的基本流程:
- 解析HTML构建Dom树,DOM 是W3C组织推荐的处理可扩展置标语言的标准编程接口。
- 构建渲染树,渲染树并不等同于Dom树,因为像
head
标签 或display: none
这样的元素就没有必要放到渲染树中了,但是它们在Dom树中。 - 对渲染树进行布局,定位坐标和大小、确定是否换行、确定position、overflow、z-index等等,这个过程叫
layout
或reflow
。 - 绘制渲染树,调用操作系统底层API(UI Backend)进行绘图操作。
webkit内核工作流程
结论:浏览器能够解析HTML文件,并且显示到页面中。所以我们写的文件能够使用浏览器打开并且能够看到效果。
性能优化:重绘与回流(重排)
回流(reflow)与重绘(repaint),在性能优化的时候,经常会提起,因为涉及到浏览器底层的渲染,所以掌握的童鞋并不多,但是面试经常被提及。
回流(reflow)
:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。
重绘(repaint)
:当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。
- 每个页面至少需要一次回流+重绘。
- 回流必将引起重绘
在此,总结会引起重排的操作有:
- 页面首次渲染。
- 浏览器窗口大小发生改变—onresize事件触发时
- 元素尺寸或位置发生改变。
- 元素内容变化(文字数量或图片大小等等)。
- 元素字体大小变化。
- 添加或者删除可见的DOM元素。
- 激活CSS伪类(例如::hover)。
- 设置style属性
- 查询某些属性或调用某些方法。
渲染队列 :
浏览器为了减少重排(回流)的次数,会将引起重排的代码加入一个渲染队列,到一定的条件再执行重排的代码,所以要把重排的代码和控制台执行打印的代码分开写,如果不分开写,会阻塞代码,造成多次重排!
以下属性或方法会刷新渲染队列
- offsetTop、offsetLeft、offsetWidth、offsetHeight
- clientTop、clientLeft、clientWidth、clientHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight、 scrollIntoView() 、scrollTo()
- getComputedStyle( )
- width 、height、 margin、 padding、 border、 position、 overflow
var s = document.body.style;
s.padding = "2px"; // 回流+重绘
s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 再一次重绘
s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
// 添加node,再一次 回流+重绘
document.body.appendChild(document.createTextNode('abc!'));
- 如何性能优化? 尽量减少重绘与回流的次数
-
直接使用className修改样式,少用style设置样式
-
让要操作的元素进行”离线处理”,处理完后一起更新
- 使用DocumentFragment进行缓存操作,引发一次回流和重绘
- 使用display:none技术,只引发两次回流和重绘;
-
将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素为动画的 HTML 元素,例如动画,那么修改他们的 CSS 是会大大减小 reflow .
-
完成功能是前提,在完成功能的情况下想着优化代码
-
var pNode,fragment = document.createDocumentFragment();
//动态创建20个p标签,先用DocumentFragment 对象来缓存
for(var i=0; i<20; i++){
pNode = document.createElement('p');
pNode.innerHTML = i;
fragment.appendChild(pNode);
}
document.body.appendChild(fragment);