zoukankan      html  css  js  c++  java
  • 浏览器渲染页面的过程,以及重排和重绘(转)

    前言

    写得比我的文字好,有逻辑!

    浏览器的渲染过程


    1,浏览器解析html源码,然后创建一个 DOM树。
    在DOM树中,每一个HTML标签都有一个对应的节点,并且每一个文本也都会有一个对应的文本节点。
    DOM树的根节点就是 documentElement,对应的是html标签。

    2,浏览器解析CSS代码,计算出最终的样式数据。
    对CSS代码中非法的语法她会直接忽略掉。
    解析CSS的时候会按照如下顺序来定义优先级:浏览器默认设置,用户设置,外链样式,内联样式,html中的style。

    3,构建出DOM树,并且计算出样式数据后,下一步就是构建一个 渲染树(rendering tree)。
    渲染树和DOM树有点像,但是是有区别的。DOM树完全和html标签一一对应,但是渲染树会忽略掉不需要渲染的元素,比如head、display:none的元素等。
    而且一大段文本中的每一个行在渲染树中都是独立的一个节点。
    渲染树中的每一个节点都存储有对应的css属性。

    4,一旦渲染树创建好了,浏览器就可以根据渲染树直接把页面绘制到屏幕上。


    一个渲染过程的例子


    例如有下面这样一段HTML代码:
    <html>
    <head>
      <title>Beautiful page</title>
    </head>
    <body>
        
      <p>
        Once upon a time there was 
        a looong paragraph...
      </p>
      
      <div style="display: none">
        Secret message
      </div>
      
      <div><img src="..." /></div>
      ...
     
    </body>
    </html>


    那么DOM树是完全和HTML标签一一对应的,如下所示:
    documentElement (html)
        head
            title
        body
            p
                [text node]
                    
            div 
                [text node]
                    
            div
                img
                    
            ...

    而渲染树就不同了,她只有哪些需要绘制出来的元素,所以head 以及被隐藏的div都不会出现在渲染树中。
    root (RenderView)
        body
            p
                line 1
                line 2
                line 3
                ...
                
            div
                img
                
            ...

    重绘和重排(repaints and reflows)

    每个页面至少在初始化的时候会有一次重排操作。任何对渲染树的修改都有可能会导致下面两种操作:
    1,重排
    就是渲染树的一部分必须要更新 并且节点的尺寸发生了变化。这就会触发重排操作。

    2,重绘
    部分节点需要更新,但是没有改变他的集合形状,比如改变了背景颜色,这就会触发重绘。


    什么情况下会触发重绘或重排


    下面任何操作都会触发重绘或者重排:

    增加或删除DOM节点
    设置 display: none;(重排并重绘) 或者 visibility: hidden(只有重排)
    移动页面中的元素
    增加或者修改样式
    用户 改变窗口大小,滚动页面等
    看一个例子:

    var bstyle = document.body.style; // cache
     
    bstyle.padding = "20px"; // reflow, repaint
    bstyle.border = "10px solid red"; // another reflow and a repaint
     
    bstyle.color = "blue"; // repaint only, no dimensions changed
    bstyle.backgroundColor = "#fad"; // repaint
     
    bstyle.fontSize = "2em"; // reflow, repaint
     
    // new DOM element - reflow, repaint
    document.body.appendChild(document.createTextNode('dude!'));

    有些重绘操作会比其他操作昂贵很多。比如你把一个body的子元素做了修改,不一定会导致大量的其他节点更新,但是你把一个元素移动到页面顶部去,可能就会导致全部其他节点进行重排操作,这个代价就非常昂贵。

    聪明的浏览器


    因为渲染树的改变导致的重绘或重排操作都可能代价很高,浏览器会对这个改动做很多优化。
    一个策略就是不要立即做操作,而是批量进行。比如把你的脚本对DOM的修改放入一个队列,在队列所有操作结束后只需要进行一次绘制即可。

    但是有的时候脚本可能会导致浏览器的批量优化无法进行,可能在清空队列之前就需要重新绘制(绘制意思是重绘或者重排)页面。比如你通过脚本获取这些样式:
    offsetTop, offsetLeft, offsetWidth, offsetHeight
    scrollTop/Left/Width/Height
    clientTop/Left/Width/Height
    getComputedStyle(), or currentStyle in IE

    因为浏览器必须给你最新的值,所以当你进行这些取值操作的时候会立刻触发一次页面的绘制。这样本来可以批量修改样式然后一次性绘制的方法就无法使用了。

    减少重绘和重排


    1,不要一个一个地单独修改属性,最好通过一个classname来定义这些修改

    // bad
    var left = 10,
        top = 10;
    el.style.left = left + "px";
    el.style.top  = top  + "px";
     
    // better 
    el.className += " theclassname";
    2,把对节点的大量修改操作放在页面之外
    用 documentFragment来做修改
    clone 节点,在clone之后的节点中做修改,然后直接替换掉以前的节点
    通过 display: none 来隐藏节点(直接导致一次重排和重绘),做大量的修改,然后显示节点(又一次重排和重绘),总共只会有两次重排。
    3,不要频繁获取计算后的样式。如果你需要使用计算后的样式,最好暂存起来而不是直接从DOM上读取。

    4,总的来说,总是考虑到渲染树得存在,考虑到你的一次修改会导致多大的绘制操作。
    比如绝对定位元素的动画就不会影响其他大部分元素。

    转自 http://blog.csdn.net/lihongxun945/article/details/37830667#

    在程序媛的路上,越走越用劲儿:)
  • 相关阅读:
    制作基于OK6410的文件系统(2.修改busybox文件系统,使之能启动)
    [转]SlickEdit 2012
    移植TPLINK TLWN721N 驱动到micro2440(by liukun321咕唧咕唧)
    擦,买到了伪劣的SD卡
    Slickedit中文显示与输入
    OMAP3530mini调试笔记(2)
    最近在做linux的移植工作
    嵌入式linux启动时运行的inittab文件(zz)
    OMAP3530mini调试笔记(1)
    Ubuntu中音量调节无效的解决方法
  • 原文地址:https://www.cnblogs.com/AliceX-J/p/5236255.html
Copyright © 2011-2022 走看看