>  기사  >  웹 프론트엔드  >  고성능 WEB 개발, 페이지 렌더링, 다시 그리기 및 리플로우.

고성능 WEB 개발, 페이지 렌더링, 다시 그리기 및 리플로우.

黄舟
黄舟원래의
2016-12-16 15:27:321328검색

페이지 렌더링 프로세스

페이지 다시 그리기 및 리플로우에 대해 논의하기 전에. 페이지 렌더링 프로세스, 페이지가 CSS와 결합된 HTML 등을 브라우저에 표시하는 방법에 대해 어느 정도 이해해야 합니다. 다음 순서도는 페이지 렌더링을 위한 브라우저의 처리 흐름을 보여줍니다. 브라우저마다 약간 다를 수 있습니다. 그러나 기본적으로 그들은 유사합니다.


1. 브라우저는 획득한 HTML 코드를 Dom 트리로 구문 분석합니다. HTML의 각 태그는 Dom 트리의 노드이고 루트 노드는 일반적으로 사용되는 문서 개체입니다. html> 태그). dom 트리는 firebug 또는 IE 개발자 도구 모음과 같은 도구를 사용하여 볼 수 있는 html 구조입니다. 여기에는 display:none Hidden을 포함한 모든 html 태그와 JS를 사용하여 동적으로 추가된 요소가 포함되어 있습니다.

2. 브라우저는 모든 스타일(주로 CSS 및 브라우저 스타일 설정 포함)을 스타일 구조로 구문 분석합니다. 구문 분석 중에 브라우저가 인식할 수 없는 스타일은 제거됩니다. _의 시작 부분에 있는 스타일을 제거하고 Firefox는 _의 시작 부분에 있는 스타일을 제거합니다.

3. DOM 트리와 스타일 구조가 결합되어 렌더 트리가 만들어집니다. 렌더 트리는 DOM 트리와 다소 유사하지만 실제로는 스타일을 식별할 수 있는 큰 차이가 있습니다. 렌더 트리의 각 렌더 트리 각 노드에는 고유한 스타일이 있으며, 렌더 트리에는 숨겨진 노드(예: display:none 노드 및 헤드 노드)가 포함되어 있지 않습니다. 왜냐하면 이러한 노드는 렌더링에 사용되지 않으며 렌더링에 영향을 주지 않기 때문입니다. , 렌더링 트리에 포함되지 않습니다. visible:hidden이 레이아웃과 점유 공간에 영향을 미치기 때문에 visible:hidden에 의해 숨겨진 요소는 여전히 렌더 트리에 포함됩니다. CSS2 표준에 따르면 렌더 트리의 각 노드를 상자(상자 크기)라고 하며 상자의 모든 속성은 너비, 높이, 여백, 패딩, 왼쪽, 위쪽, 테두리 등입니다.

4. 렌더 트리가 구성되면 브라우저는 렌더 트리를 기반으로 페이지를 그릴 수 있습니다.

리플로우 및 다시 그리기

1. 요소의 크기, 레이아웃, 숨기기 등의 변경으로 인해 렌더 트리의 일부(또는 전체)를 다시 작성해야 하는 경우. 이것을 리플로우(reflow)라고 합니다. (실제로는 재배열이라고 부르는 것이 더 간단하고 명확하다고 생각합니다.) 모든 페이지는 페이지가 처음 로드될 때 적어도 한 번은 리플로우되어야 합니다.

2. 렌더 트리의 일부 요소가 속성을 업데이트해야 하는 경우 이러한 속성은 요소의 모양과 스타일에만 영향을 미치고 배경색과 같은 레이아웃에는 영향을 미치지 않습니다. 다시 그리는 일이라고 합니다.
참고: 위에서 볼 수 있듯이 리플로우로 인해 반드시 다시 그리기가 발생하지만 다시 그리기로 인해 반드시 리플로우가 발생하지는 않습니다.

어떤 작업으로 인해 다시 그리기 및 리플로우가 발생하나요?
실제로 렌더 트리의 요소에 대한 모든 작업은 다음과 같이 리플로우나 다시 그리기를 유발합니다.

1. (리플로우+다시 그리기)

2. 요소 숨기기, 표시: 없음(리플로우 + 다시 그리기), 가시성: 숨김(다시 그리기만, 리플로우 없음)

3. 상단 변경 등 요소 이동, 왼쪽(jquery의 애니메이션 방법은 위쪽과 왼쪽을 변경해도 반드시 리플로우에 영향을 미치지는 않는다는 것입니다.) 또는 요소를 다른 상위 요소로 이동합니다. (다시 그리기 + 리플로우)

4. 스타일 작업(속성 작업에 따라 효과가 다름)

5. 브라우저 크기 변경, 브라우저 글꼴 변경 등 사용자 작업도 있습니다. 크기 등 (리플로우 + 다시 그리기)

다음 코드가 리플로우 및 다시 그리기에 어떤 영향을 미치는지 살펴보겠습니다.

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!'));

How Many를 다시 사용했음을 참고하세요.

말하자면 리플로우 비용은 다시 그리는 것보다 비용이 더 많이 든다는 것은 렌더 트리에서 다시 빌드해야 하는 노드 수와 관련이 있습니다. 1. 요소는 전체 렌더 트리를 리플로우하게 하므로 비용이 더 많이 듭니다. 그러나 요소가 바디 뒤에 삽입되면 이전 요소의 리플로우에 영향을 미치지 않습니다.

聪明的浏览器

     从上个实例代码中可以看到几行简单的JS代码就引起了6次左右的回流、重绘。而且我们也知道回流的花销也不小,如果每句JS操作都去回流重绘的话,浏览器可能就会受不了。所以很多浏览器都会优化这些操作,浏览器会维护1个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会把flush队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。

    虽然有了浏览器的优化,但有时候我们写的一些代码可能会强制浏览器提前flush队列,这样浏览器的优化可能就起不到作用了。当你请求向浏览器请求一些style信息的时候,就会让浏览器flush队列,比如:
    1. offsetTop, offsetLeft, offsetWidth, offsetHeight
    2. scrollTop/Left/Width/Height
    3. clientTop/Left/Width/Height
    4. width,height
    5. 请求了getComputedStyle(), 或者 ie的 currentStyle

    当你请求上面的一些属性的时候,浏览器为了给你最精确的值,需要flush队列,因为队列中可能会有影响到这些值的操作。

如何减少回流、重绘

    减少回流、重绘其实就是需要减少对render tree的操作,并减少对一些style信息的请求,尽量利用好浏览器的优化策略。具体方法有:

    1. 不要1个1个改变元素的样式属性,最好直接改变className,但className是预先定义好的样式,不是动态的,如果你要动态改变一些样式,则使用cssText来改变,见下面代码:

// 不好的写法
var left = 1;
var top = 1;
el.style.left = left + "px";
el.style.top  = top  + "px";
// 比较好的写法 
el.className += " className1";
// 比较好的写法 
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

2. 让要操作的元素进行"离线处理",处理完后一起更新,这里所谓的"离线处理"即让元素不存在于render tree中,比如:
        a) 使用documentFragment或div等元素进行缓存操作,这个主要用于添加元素的时候,大家应该都用过,就是先把所有要添加到元素添加到1个div(这个div也是新加的),
            最后才把这个div append到body中。
        b) 先display:none 隐藏元素,然后对该元素进行所有的操作,最后再显示该元素。因对display:none的元素进行操作不会引起回流、重绘。所以只要操作只会有2次回流。

   3 不要经常访问会引起浏览器flush队列的属性,如果你确实要访问,就先读取到变量中进行缓存,以后用的时候直接读取变量就可以了,见下面代码:

// 别这样写,大哥
for(循环) {
    el.style.left = el.offsetLeft + 5 + "px";
    el.style.top  = el.offsetTop  + 5 + "px";
}
// 这样写好点
var left = el.offsetLeft,top  = el.offsetTop,s = el.style;
for(循环) {
    left += 10;
    top  += 10;
    s.left = left + "px";
    s.top  = top  + "px";
}

   4. 考虑你的操作会影响到render tree中的多少节点以及影响的方式,影响越多,花费肯定就越多。比如现在很多人使用jquery的animate方法移动元素来展示一些动画效果,想想下面2种移动的方法:

// block1是position:absolute 定位的元素,它移动会影响到它父元素下的所有子元素。

// 因为在它移动过程中,所有子元素需要判断block1的z-index是否在自己的上面,
// 如果是在自己的上面,则需要重绘,这里不会引起回流
$("#block1").animate({left:50});

// block2是相对定位的元素,这个影响的元素与block1一样,但是因为block2非绝对定位
// 而且改变的是marginLeft属性,所以这里每次改变不但会影响重绘,
// 还会引起父元素及其下元素的回流

$("#block2").animate({marginLeft:50});


实例测试

    最后用2个工具对上面的理论进行一些测试,这2个工具是在我 "web 性能测试工具推荐" 文章中推荐过的工具,分别是:dynaTrace(测试ie),Speed Tracer(测试Chrome)。

    第一个测试代码不改变元素的规则,大小,位置。只改变颜色,所以不存在回流,仅测试重绘,代码如下:

<body>
    <script type="text/javascript">
        var s = document.body.style;
        var computed;
        if (document.body.currentStyle) {
          computed = document.body.currentStyle;
        } else {
          computed = document.defaultView.getComputedStyle(document.body, &#39;&#39;);
        }
    function testOneByOne(){
      s.color = &#39;red&#39;;;
      tmp = computed.backgroundColor;
      s.color = &#39;white&#39;;
      tmp = computed.backgroundImage;
      s.color = &#39;green&#39;;
      tmp = computed.backgroundAttachment;
    }
    function testAll() {
      s.color = &#39;yellow&#39;;
      s.color = &#39;pink&#39;;
      s.color = &#39;blue&#39;;
      tmp = computed.backgroundColor;
      tmp = computed.backgroundImage;
      tmp = computed.backgroundAttachment;
    }
    </script>    
    color test <br />
    <button onclick="testOneByOne()">Test One by One</button>
    <button onclick="testAll()">Test All</button>
</body>

 testOneByOne 函数改变3次color,其中每次改变后调用getComputedStyle,读取属性值(按我们上面的讨论,这里会引起队列的flush),testAll 同样是改变3次color,但是每次改变后并不马上调用getComputedStyle。
我们先点击Test One by One按钮,然后点击 Test All,用dynaTrace监控如下:


고성능 WEB 개발, 페이지 렌더링, 다시 그리기 및 리플로우.

上图可以看到我们执行了2次button的click事件,每次click后都跟一次rendering(页面重绘),2次click函数执行的时间都差不多,0.25ms,0.26ms,但其后的rendering时间就相差一倍多。(这里也可以看出,其实很多时候前端的性能瓶颈并不在于JS的执行,而是在于页面的呈现,这种情况在用JS做到富客户端中更为突出)。我们再看图的下面部分,这是第一次rendering的详细信息,可以看到里面有2行是 Scheduleing layout task,这个就是我们前面讨论过的浏览器优化过的队列,可以看出我们引发2次的flush。

고성능 WEB 개발, 페이지 렌더링, 다시 그리기 및 리플로우.

   再看第二次rendering的详细信息,可以看出并没有Scheduleing layout task,所以这次rendering的时间也比较短。


  测试代码2:这个测试跟第一次测试的代码很类似,但加上了对layout的改变,为的是测试回流。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
    <script type="text/javascript">
        var s = document.body.style;
        var computed;
        if (document.body.currentStyle) {
          computed = document.body.currentStyle;
        } else {
          computed = document.defaultView.getComputedStyle(document.body, &#39;&#39;);
        }
    function testOneByOne(){
      s.color = &#39;red&#39;;
      s.padding = &#39;1px&#39;;
      tmp = computed.backgroundColor;
      s.color = &#39;white&#39;;
      s.padding = &#39;2px&#39;;
      tmp = computed.backgroundImage;
      s.color = &#39;green&#39;;
      s.padding = &#39;3px&#39;;
      tmp = computed.backgroundAttachment;
    }
    function testAll() {
      s.color = &#39;yellow&#39;;
      s.padding = &#39;4px&#39;;
      s.color = &#39;pink&#39;;
      s.padding = &#39;5px&#39;;
      s.color = &#39;blue&#39;;
      s.padding = &#39;6px&#39;;
      tmp = computed.backgroundColor;
      tmp = computed.backgroundImage;
      tmp = computed.backgroundAttachment;
    }
    </script>    
    color test <br />
    <button onclick="testOneByOne()">Test One by One</button>
    <button onclick="testAll()">Test All</button>
</body>

  用dynaTrace监控如下: 


고성능 WEB 개발, 페이지 렌더링, 다시 그리기 및 리플로우.

相信这图不用多说大家都能看懂了吧,可以看出有了回流后,rendering的时间相比之前的只重绘,时间翻了3倍了,可见回流的高成本性啊。
  大家看到时候注意明细处相比之前的多了个 Calcalating flow layout。


  最后再使用Speed Tracer测试一下,其实结果是一样的,只是让大家了解下2个测试工具:

  测试1:

고성능 WEB 개발, 페이지 렌더링, 다시 그리기 및 리플로우.

图上第一次点击执行2ms(其中有50% 用于style Recalculation), 第二次1ms,而且第一次click后面也跟了2次style Recalculation,而第二次点击却没有style Recalculation。
  但是这次测试发现paint重绘的时间竟然是一样的,都是3ms,这可能就是chrome比IE强的地方吧。

  测试2:

고성능 WEB 개발, 페이지 렌더링, 다시 그리기 및 리플로우.

从图中竟然发现第二次的测试结果在时间上跟第一次的完全一样,这可能是因为操作太少,而chrome又比较强大,所以没能测试明显结果出来,
但注意图中多了1个紫色部分,就是layout的部分。也就是我们说的回流。

 以上就是高性能WEB开发 页面呈现、重绘、回流。的内容,更多相关文章请关注PHP中文网(www.php.cn)! 


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.