Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

网页的渲染过程及优化 #1

Open
q837477816 opened this issue Mar 1, 2018 · 0 comments
Open

网页的渲染过程及优化 #1

q837477816 opened this issue Mar 1, 2018 · 0 comments

Comments

@q837477816
Copy link
Owner

网页的渲染过程以及优化

页面生成的过程

  1. 浏览器识别HTML代码,按照深度优先的原则解析每一个节点(包括display: none的节点)形成一个DOMTree。
  2. 浏览器把所有样式(用户定义的CSS和用户代理)解析成样式结构体即CSSOM(CSS Object Model),在解析的过程中会去掉浏览器不能识别的样式,比如IE会去掉-moz开头的样式,而FF会去掉_开头的样式。
  3. 将DOMTree和CSSOM结合,生成一颗renderTree渲染树。renderTree能识别样式,包含每个节点的视觉信息。注意renderTree不包含隐藏的节点 (比如display:none的节点,还有head节点),因为这些节点不会用于呈现,而且不会影响呈现的,所以就不会包含到 renderTree中。注意 visibility:hidden隐藏的元素还是会包含到 render tree中的,因为visibility:hidden 会影响布局(layout),会占有空间。
  4. 生成布局(layout),即将所有渲染树的所有节点进行平面合成。
  5. 布局绘制(paint)在屏幕上。

注意这5步中,耗时的是第4步和第5步,前3步都很快。
生成布局(flow)和绘制(paint)这两步合称为渲染。

重绘(repaint)和重排(reflow)

网页生成的时候,至少会渲染一次。用户访问的过程中,还会不断重新渲染。
以下三种情况,会导致网页重新渲染。

  1. 修改DOM
  2. 修改样式表
  3. 用户事件(比如鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等等)

重新渲染,就需要重新生成布局和重新绘制。前者叫做"重排"(reflow),后者叫做"重绘"(repaint)。

需要注意的是,"重绘"不一定需要"重排",比如改变某个网页元素的颜色,就只会触发"重绘",不会触发"重排",因为布局没有改变。但是,"重排"必然导致"重绘",比如改变一个网页元素的位置,就会同时触发"重排"和"重绘",因为布局改变了。

重绘和重排对性能的影响

重排和重绘会不断触发,这是不可避免的。但是,它们非常耗费资源,是导致网页性能低下的根本原因。
提高网页性能,就是要降低"重排"和"重绘"的频率和成本,尽量少触发重新渲染。

前面提到,DOM变动和样式变动,都会触发重新渲染。但是,浏览器已经很智能了,会尽量把所有的变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。
div.style.color = 'blue';
div.style.marginTop = '30px';
上面代码中,div元素有两个样式变动,但是浏览器只会触发一次重排和重绘。

如果写得不好,就会触发两次重排和重绘。
div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';
上面代码对div元素设置背景色以后,第二行要求浏览器给出该元素的位置,所以浏览器不得不立即重排。

一般来说,样式的写操作之后,如果有下面这些属性的读操作,都会引发浏览器立即重新渲染。
offsetTop/offsetLeft/offsetWidth/offsetHeight
scrollTop/scrollLeft/scrollWidth/scrollHeight
clientTop/clientLeft/clientWidth/clientHeight
getComputedStyle()

所以,从性能角度考虑,尽量不要把读操作和写操作,放在一个语句里面。
// bad
div.style.left = div.offsetLeft + 10 + "px";
div.style.top = div.offsetTop + 10 + "px";
// good
var left = div.offsetLeft;
var top = div.offsetTop;
div.style.left = left + 10 + "px";
div.style.top = top + 10 + "px";

一般的规则是:

  1. 样式表越简单,重排和重绘就越快。
  2. 重排和重绘的DOM元素层级越高,成本就越高。
  3. table元素的重排和重绘成本,要高于div元素

网页性能优化的技巧

  1. DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。

  2. 如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排。

  3. 不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式。
    // bad
    var left = 10;
    var top = 10;
    el.style.left = left + "px";
    el.style.top = top + "px";
    // good
    el.className += " theclassname";
    // good
    el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

  4. 尽量使用离线DOM,而不是真实的网面DOM,来改变元素样式。比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。

  5. 先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。

  6. position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。

  7. 只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。

  8. 使用虚拟DOM的脚本库,比如React等。

  9. 使用 window.requestAnimationFrame()、window.requestIdleCallback() 这两个方法调节重新渲染。

参考链接

http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html

@q837477816 q837477816 changed the title 2018.3.1 网页的渲染过程及优化 Mar 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant