渲染引擎经过经过网络请求接收渲染内容javascript
渲染引擎的第一步是解析html文档并将解析的元素转换为dom树中的实际dom节点。
![]()
当浏览器解析dom的时候,遇到link标签,引用外部的css样式表,引擎会将css抽象成cssom
![]()
HTML中的可视指令与来自cssom树的样式数据结合使用来建立渲染树。
![]()
为了构建渲染树,浏览器大体以下:
它包括几何信息,如宽度,高度和位置css
当渲染器被建立并添加到树中时,它没有位置和大小。计算这些值称为布局。html使用基于流的布局模型,这意味着大多数时候它能够一次性计算几何。坐标系相对于根渲染器。使用顶部和左侧坐标。html
布局是一个递归过程,从根元素开始,也就是html,每一个渲染器都会去计算他本身的位置和大小html5
在这个阶段,遍历渲染器树,调用渲染器的paint()方法在屏幕上显示内容。
渲染分为全局渲染和增量渲染java
当解析器到达script标记时,脚本将被当即解析并执行。
文档的解析将暂停,直到脚本执行完毕。
这意味着该过程是同步的
这也是为何把script标签放在body结束以前web
html5添加了一个选项,将脚本标记为异步,以便它能够被其余线程解析和执行。json
repaint重绘:浏览器
reflow回流:缓存
减小reflow和repaint网络
javascript
CSS
// 设置单个属性 elt.style.color = "blue"; // 在单个语句中设置多个样式 elt.style.cssText = "color: blue; border: 1px solid black"; // 在单个语句中设置多个样式 elt.setAttribute("style", "color:red; border: 1px solid blue;");
DOM
这个过程会触发两次回流,第一步和第三步。把会触发屡次回流的步骤放在第二步
三种基本方法:
var fragment = document.createDocumentFragment() ... 在这里进行dom操做,能够减小回流和重绘的次数 document.getElementById('#app').appendChild(fragment)
var old = document.getElementById('#app') var clone = old.cloneNode(true) ... 在这里进行dom操做,能够减小回流和重绘的次数 old.parentNode.replaceChild(clone, old)
缓存布局信息
前面提到在查询布局信息(offsetLeft...)的时候也会引发回流,咱们在使用的时候能够把布局信息缓存起来,减小回流次数
这里贴上<<高性能javascript>>中的例子:把myElement元素沿对角线移动,每次移动一个像素,从100100的位置开始,到500500的位置结束。在timeout循环体中你可使用下面的方法
// 低效的 myElement.style.left = 1 + myElement.offsetLeft + 'px' myElement.style.top = 1 + myElement.offsetTop + 'px' if (myElement.offsetTop >= 500) { stopAnimation(); }
// 优化 // 在循环外层获取初始值 var current = myElement.offsetLeft . . . // 直接使用current变量,再也不查询偏移量 current++ myElement.style.left = current + 'px' myElement.style.top = current + 'px' if (current >= 500) { stopAnimation(); }
使元素进行动画效果的时候脱离文档流
在元素发生动画效果的时候,会引发底部元素的回流,这个影响可能很大,也可能很小,取决于元素在文档流的位置
参考DOM操做成本到底高在哪儿
参考高性能javascript
参考How JavaScript works: the rendering engine and tips to optimize its performance