Maison  >  Article  >  interface Web  >  Méthodes d'optimisation pour le redessinage et la redistribution des pages

Méthodes d'optimisation pour le redessinage et la redistribution des pages

一个新手
一个新手original
2017-09-18 09:34:493088parcourir

Avant que la page de discussion soit redessinée et redistribuée. Vous devez avoir une certaine compréhension du processus de rendu des pages, de la façon dont la page affiche le HTML combiné avec le CSS, etc. dans le navigateur. L'organigramme suivant montre le flux de traitement du navigateur pour le rendu des pages. Différents navigateurs peuvent être légèrement différents. Mais au fond, ils sont similaires.
Méthodes doptimisation pour le redessinage et la redistribution des pages

  1. Le navigateur analyse le code HTML obtenu dans une arborescence DOM. Chaque balise HTML est un nœud dans l'arborescence DOM, et le nœud racine est notre nœud couramment utilisé. objet de document. L'arborescence DOM contient toutes les balises HTML, y compris display:none masqué, les éléments ajoutés dynamiquement à l'aide de JS, etc.

  2. Le navigateur analyse tous les styles (CSS définis par l'utilisateur et agents utilisateurs) en structures de style Pendant le processus d'analyse, les styles qui ne sont pas reconnus par le navigateur seront supprimés. IE les supprimera les styles commençant par -moz, tandis que FF supprimera les styles commençant par _.

3. L'arbre de rendu et la structure de style sont combinés pour créer l'arbre de rendu. L'arbre de rendu est similaire à l'arbre de rendu, mais il est très différent. a son propre style et l'arborescence de rendu ne contient pas de nœuds cachés (tels que les nœuds display:none et les nœuds principaux). Parce que ces nœuds ne seront pas utilisés pour le rendu et n'affecteront pas le rendu, ils ne seront pas inclus dans le rendu. arbre. Notez que les éléments masqués par visibilité:hidden seront toujours inclus dans l'arborescence de rendu, car visibilité:hidden affectera la mise en page et occupera de l'espace. Selon la norme CSS2, chaque nœud de l'arbre de rendu est appelé Box (dimensions de la boîte) et l'élément de page est compris comme une boîte avec un remplissage, des marges, des bordures et une position.

  1. Une fois l'arbre de rendu construit, le navigateur peut dessiner la page en fonction de l'arbre de rendu.

Redistribuer et redessiner

  1. Lorsqu'une partie (ou la totalité) de l'arbre de rendu est due à la taille du L'élément, la mise en page, le masquage, etc. changent et doivent être reconstruits. C'est ce qu'on appelle la refusion. Chaque page doit être redistribuée au moins une fois, lors du premier chargement de la page. Pendant la redistribution, le navigateur invalidera la partie affectée de l'arborescence de rendu et reconstruira cette partie de l'arborescence de rendu. Après avoir terminé la redistribution, le navigateur redessinera la partie affectée à l'écran. Ce processus est appelé redessin.

  2. Lorsque certains éléments de l'arbre de rendu doivent mettre à jour leurs attributs, ces attributs affectent uniquement l'apparence et le style des éléments, mais n'affecteront pas la mise en page, comme la couleur d'arrière-plan. Cela s’appelle redessiner.

Remarque : la redistribution entraînera certainement un redessinage, mais le redessinage ne provoquera pas nécessairement un reécoulement.

Lorsque la redistribution se produit :

La redistribution est nécessaire lorsque la mise en page et les propriétés géométriques changent. La redistribution du navigateur se produira dans les circonstances suivantes :

1. Ajout ou suppression d'éléments DOM visibles ;

2. La position de l'élément change ;

3. l'élément change —— Marges, remplissage, bordures, largeur et hauteur

4. Modifications du contenu - telles que les modifications de la largeur et de la hauteur des valeurs calculées causées par des modifications du texte ou des modifications de la taille de l'image

5. Initialisation du rendu de la page ;

6. La taille de la fenêtre du navigateur change - lorsque l'événement de redimensionnement se produit ;

Voyons comment le code suivant affecte la redistribution et le redessin :

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

En parlant de ça, tout le monde sait que la redistribution coûte plus cher que le redessinage. Le coût de la redistribution est lié au nombre de nœuds dans l'arborescence de rendu qui doivent être reconstruits. Supposons que vous exploitiez directement le corps, comme insérer un élément à l'avant de. le corps, cela entraînera la refusion de tout l'arbre de rendu, ce qui sera bien sûr plus cher, mais si vous insérez un élément après le corps, cela n'affectera pas la refusion de l'élément précédent

Navigateur intelligent

À partir de l'exemple de code précédent, vous pouvez voir que quelques lignes de code JS simple ont provoqué environ 6 redistributions et redessins. Et nous savons également que le coût de la redistribution n'est pas minime. Si chaque opération JS doit être redistribuée et redessinée, le navigateur ne pourra peut-être pas le supporter. Par conséquent, de nombreux navigateurs optimiseront ces opérations. Le navigateur maintiendra une file d'attente et placera toutes les opérations qui provoqueront une redistribution et un redessinage dans cette file d'attente. Lorsque les opérations dans la file d'attente atteignent un certain nombre ou un certain intervalle de temps, le navigateur La file d'attente. être vidé et un lot sera traité. Cela transformera plusieurs refusions et redessinages en une seule redistribution et redessinage.

Bien qu'il existe des optimisations du navigateur, parfois une partie du code que nous écrivons peut forcer le navigateur à vider la file d'attente à l'avance, de sorte que l'optimisation du navigateur peut ne pas être efficace. Lorsque vous demandez des informations de style au navigateur, le navigateur vide la file d'attente, par exemple :

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
    scrollTop/Left/Width/Height
    clientTop/Left/Width/Height
    width,height
    请求了getComputedStyle(), 或者 IE的 currentStyle

Lorsque vous demandez certaines des informations ci-dessus Lors de la configuration attributs, afin de vous donner la valeur la plus précise, le navigateur doit vider la file d'attente, car il peut y avoir des opérations dans la file d'attente qui affectent ces valeurs. Même si les informations de mise en page et de style que vous obtenez pour un élément n'ont rien à voir avec les informations de mise en page récemment survenues ou modifiées, le navigateur actualisera de force la file d'attente de rendu.

Comment réduire la refusion et redessiner

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

  1. 直接改变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;";
  1. 让要操作的元素进行”离线处理”,处理完后一起更新

a) 使用 DocumentFragment 进行缓存操作,引发一次回流和重绘;
b) 使用 display:none 技术,只引发两次回流和重绘;
c) 使用  cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;

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"; }
  1. 让元素脱离动画流,减少回流的Render Tree的规模

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

实例测试

最后用2个工具对上面的理论进行一些测试,分别是: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监控如下:
Méthodes doptimisation pour le redessinage et la redistribution des pages

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

Méthodes doptimisation pour le redessinage et la redistribution des pages

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

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

 <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>

Méthodes doptimisation pour le redessinage et la redistribution des pages

这图可以看出,有了回流后,rendering的时间相比之前的只重绘,时间翻了3倍了,可见回流的高成本性啊。

大家看到时候注意明细处相比之前的多了个 Calcalating flow layout。

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

测试1:
Méthodes doptimisation pour le redessinage et la redistribution des pages

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

测试2:
Méthodes doptimisation pour le redessinage et la redistribution des pages

从图中竟然发现第二次的测试结果在时间上跟第一次的完全一样,这可能是因为操作太少,而chrome又比较强大,所以没能测试明显结果出来。

但注意图中多了1个紫色部分,就是layout的部分。也就是我们说的回流。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn