// bad
function resizeAllParagraphsToMatchBlockWidth() {
for (var i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = box.offsetWidth + 'px';
}
}
// better
var width = box.offsetWidth;
function resizeAllParagraphsToMatchBlockWidth() {
for (var i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = width + 'px';
}
}
3.4、 设置具有动画效果的DOM元素为固定定位
使用绝对定位使得该元素在渲染树中成为 body 下的一个直接子节点,因此当它进行动画时,它不会影响太多其他节点。
1、浏览器渲染原理
在讲DOM操作的最佳性能实践之前,先介绍下浏览器的基本渲染原理。浏览器渲染展示网页的主流程大致可以用下图表示:
(图:WebKit 主流程)
分为以下四个步骤:
解析HTML(HTML Parser)
构建DOM树(DOM Tree)
渲染树构建(Render Tree)
绘制渲染树(Painting)
需要着重指出的是,这是一个渐进的过程。为达到更好的用户体验,浏览器会力求尽快将内容显示在屏幕上。它不必等到整个 HTML 文档解析完毕之后,就会开始构建呈现树和设置布局。在不断接收和处理来自网络的其余内容的同时,浏览器会将部分内容解析并显示出来。
2、Repaints and reflows
Repaint:可以理解为重绘或重画,当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,例如改变背景颜色 。则就叫称为重绘。 Reflows:可以理解为回流、布局或者重排,当渲染树(render Tree)中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow),也就是重新布局(relayout)。
回流或者重绘何时触发?
改变用于构建渲染树的任何内容都可能导致重绘或回流,例如: 1、添加,删除,更新DOM节点 2、用display: none(回流和重绘)或者visibility: hidden隐藏节点(只有重绘,因为没有几何更改) 3、添加样式表,调整样式属性 4、调整窗口大小,更改字体大小 5、页面初始化的渲染 6、移动DOM元素 。。。
我们来看几个例子:
3、DOM操作最佳实践
DOM操作带来的页面 Repaints 和 Reflows 是不可避免的,但可以遵循一些最佳实践来最大限度地减少Repaints 和 Reflows。如下是一些具体的实践方法:
3.1、合并多次的DOM操作
以上所有这些基本上都是请求有关节点的样式信息,浏览器必须提供最新的值。 为了做到这一点,它需要应用所有计划的更改,刷新队列,强行回流。所以在有大批量DOM操作时,应避免获取DOM元素的布局信息,使得浏览器针对大批量DOM操作的优化不被破坏。如果需要这些布局信息,最好是在DOM操作之前就去获取。
3.2、让DOM元素脱离渲染树(render Tree)后修改
(1)使用文档片段 DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的孩子所代替。因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(Reflow)。当然,最后一步把文档片段附加到页面的这一步操作还是会造成回流(Reflow)。
(2)通过设置DOM元素的display样式为none来隐藏元素 原理是先隐藏元素,然后基于元素做DOM操作,经过大量的DOM操作后才把元素显示出来。
(3)克隆DOM元素到内存中 这种方式是把页面上的DOM元素克隆一份到内存中,然后再在内存中操作克隆的元素,操作完成后使用此克隆元素替换页面中原来的DOM元素。
3.3、使用局部变量缓存样式信息
获取DOM的样式信息会有性能的损耗,所以如果存在循环调用,最佳的做法是尽量把这些值缓存在局部变量中。
3.4、 设置具有动画效果的DOM元素为固定定位
使用绝对定位使得该元素在渲染树中成为 body 下的一个直接子节点,因此当它进行动画时,它不会影响太多其他节点。
4、具体例子
4.1、浏览器的批处理及回流
以下会通过一个具体例子来说明,链接地址如下:reflow。
第一次点击的代码如下:
第二次点击的代码如下:
以下我们将通过谷歌工具来查看这两次操作有什么异同。
4.1.1、首先用谷歌浏览器打开如上的链接。按下F12,切换到Performance选项
结果如下图:
4.1.2、按下ctrl + E(或者点击小圆点)开始录制,点击 body 区域,待文字变成绿色后点击“stop”停止录制
结果如下图:
4.1.3、选中上图中蓝色(js堆)突然升高的部分,表示刚才点击body的过程,滚动鼠标放大主线程
结果如下图,注意箭头指的地方:
4.1.4、点击圆点旁边的clear按钮清空,重复上述的操作,直到文字变蓝色停止:
结果如下图,注意箭头指的地方:
4.2、频繁回流造成的影响
谷歌文档给的例子,链接地址如下:animation。
优化前的代码:
优化后的代码: