検索
ホームページウェブフロントエンドhtmlチュートリアルDOM 操作が遅いのはなぜですか_html/css_WEB-ITnose

転載元: http://kb.cnblogs.com/page/534571/

DOM は非常に遅いので、DOM の操作はできるだけ少なくするべきだといつも聞いていたので、さらに詳しく説明したいと思いました。みんながそう言う理由を探ってください。私はオンラインでいくつかの情報を調べてここにまとめました。

まず、DOM オブジェクト自体も js オブジェクトなので、厳密に言えば、このオブジェクトの操作が遅いということではなく、このオブジェクトを操作した後に、レイアウトやペイントなどのブラウザーの動作がいくつかトリガーされます。 。以下では、主にこれらのブラウザーの動作を紹介し、ページが最終的にどのように表示されるかについて説明します。さらに、コードの観点からいくつかの悪い習慣といくつかの最適化ソリューションについても説明します。

ブラウザがページをレンダリングする仕組み

ブラウザには多くのモジュールがあり、その中でレンダリング エンジン モジュールは、WebKit や Gecko などのより身近なモジュールです。ここでは、このモジュールの内容のみを説明します。

このプロセスを言葉で簡単に説明しましょう:

  • HTMLを解析してDOMツリーを生成する

  • さまざまなスタイルを解析し、DOMツリーと結合してレンダーツリーを生成する

  • レンダーツリーの各ノードに対してボックスの位置やサイズなどのレイアウト情報を計算します

  • Render ツリーに従って描画し、ブラウザの UI レイヤーを使用します

  • このうち、DOM ツリーと Render ツリー上のノードは一対一ではありません-one の対応関係 (「display:none」など) このノードは DOM ツリー上にのみ存在し、このノードを描画する必要がないため、Render ツリーには表示されません。

    上の図はWebkitの基本的な処理です。Geckoのフローチャートとは用語が異なる場合がありますが、以下の記事内容はWebkitの用語を統一して使用します。

    最初の画面レンダリングに影響を与えるリンクの位置など、ページのレンダリングに影響を与える要素は数多くあります。ただし、ここでは主にレイアウト関連のコンテンツに焦点を当てます。

    ペイントは時間のかかるプロセスですが、レイアウトはさらに時間がかかるプロセスです。レイアウトが 1 つでも、ドキュメント全体の再レイアウトが必要になるため、トップダウンにする必要があるかどうかはわかりません。 . 計算します。

    しかし、レイアウトは間違いなく避けられないので、私たちの主な目標はレイアウトの数を最小限に抑えることです。

    ブラウザはどのような状況でレイアウトを実行しますか?

    レイアウトの数を最小限に抑える方法を考える前に、まずブラウザがいつレイアウトを実行するかを理解する必要があります。

    レイアウト(リフロー)は一般にレイアウトと呼ばれ、ドキュメント内の要素の位置とサイズを計算するために使用されます。レンダリングする前の重要なステップです。 HTML が初めて読み込まれるとき、レイアウトに加えて、js スクリプトの実行とスタイルの変更もブラウザーにレイアウトを実行させます。これは、この記事で説明する主な内容でもあります。

    通常の状況では、ブラウザのレイアウトは遅延しています。つまり、js スクリプトが実行されると、DOM への変更は一時的にキューに保存され、更新されません。コンテキストの実行が完了すると、このキュー内の変更に基づいてレイアウトが実行されます。

    ただし、js コードで最新の DOM ノード情報をすぐに取得したい場合、ブラウザーが事前にレイアウトを実行する必要がある場合があり、これが DOM パフォーマンスの問題の主な原因です。

    次の操作はルーチンを中断し、ブラウザでレイアウトを実行するようにトリガーします:

  • js を通じて計算する必要がある DOM 属性を取得する

  • DOM 要素を追加または削除する

  • ブラウザのウィンドウ サイズを変更する

  • Change Font

  • css疑似クラスのアクティブ化、例: hover

  • jsを介してDOM要素のスタイルを変更し、そのスタイルにはサイズ変更が含まれます

  • 例を通して直感的に感じてみましょう:

    // Readvar h1 = element1.clientHeight;// Write (invalidates layout)element1.style.height = (h1 * 2) + 'px';// Read (triggers layout)var h2 = element2.clientHeight;// Write (invalidates layout)element2.style.height = (h2 * 2) + 'px';// Read (triggers layout)var h3 = element3.clientHeight;// Write (invalidates layout)element3.style.height = (h3 * 2) + 'px';

    clientHeight、この属性は計算する必要があるため、ブラウザーのレイアウトがトリガーされます。 Chrome (v47.0) の開発者ツールを使用して見てみましょう (スクリーンショットのタイムライン レコードはフィルタリングされ、レイアウトのみが表示されています):

    上記の例では、コードは最初にスタイルを変更します。要素を読み取ってから、別の要素の clientHeight 属性を読み取ります。前の変更により、現在の DOM はダーティとしてマークされます。この属性を確実に取得できるようにするために、ブラウザはレイアウトを実行します (Chrome の開発者が確認しました)。ツールは良心的にこのパフォーマンスの問題を私たちに思い出させてくれました)。

    このコードの最適化は非常に簡単で、必要な属性を事前に読み込んで一緒に変更するだけです。

    // Readvar h1 = element1.clientHeight;  var h2 = element2.clientHeight;  var h3 = element3.clientHeight;// Write (invalidates layout)element1.style.height = (h1 * 2) + 'px';  element2.style.height = (h2 * 2) + 'px';  element3.style.height = (h3 * 2) + 'px';

    今回の状況を見てください:

    他の最適化ソリューションをいくつか紹介しましょう。

    レイアウトを最小化するためのスキーム

    上記のバッチ読み取りと書き込みは、主に計算が必要な属性値を取得することによって発生します。それでは、どの値を計算する必要があるでしょうか。

    このリンクでは、計算する必要がある属性のほとんどを紹介しています: http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html

    見てみましょうその他の状況:

      面对一系列DOM操作

      针对一系列DOM操作(DOM元素的增删改),可以有如下方案:

  • documentFragment

  • display: none

  • cloneNode

  •   比如(仅以documentFragment为例):

    var fragment = document.createDocumentFragment();  for (var i=0; i < items.length; i++){    var item = document.createElement("li");  item.appendChild(document.createTextNode("Option " + i);  fragment.appendChild(item);}list.appendChild(fragment);

      这类优化方案的核心思想都是相同的,就是先对一个不在Render tree上的节点进行一系列操作,再把这个节点添加回Render tree,这样无论多么复杂的DOM操作,最终都只会触发一次layout。

      面对样式的修改

      针对样式的改变,我们首先需要知道并不是所有样式的修改都会触发layout,因为我们知道layout的工作是计算RenderObject的尺寸和大小信息,那么我如果只是改变一个颜色,是不会触发layout的。

      这里有一个网站CSS triggers,详细列出了各个CSS属性对浏览器执行layout和paint的影响。

      像下面这种情况,和上面讲优化的部分是一样的,注意下读写即可。

    elem.style.height = "100px"; // mark invalidated  elem.style.width = "100px";  elem.style.marginRight = "10px";elem.clientHeight // force layout here

      但是要提一下动画,这边讲的是js动画,比如:

    function animate (from, to) {    if (from === to) return  requestAnimationFrame(function () {    from += 5    element1.style.height = from + "px"    animate(from, to)  })}animate(100, 500)

      动画的每一帧都会导致layout,这是无法避免的,但是为了减少动画带来的layout的性能损失,可以将动画元素绝对定位,这样动画元素脱离文本流,layout的计算量会减少很多。

      使用requestAnimationFrame

      任何可能导致重绘的操作都应该放入requestAnimationFrame

      在现实项目中,代码按模块划分,很难像上例那样组织批量读写。那么这时可以把写操作放在requestAnimationFrame的callback中,统一让写操作在下一次paint之前执行。

    // Readvar h1 = element1.clientHeight;// WriterequestAnimationFrame(function() {    element1.style.height = (h1 * 2) + 'px';});// Readvar h2 = element2.clientHeight;// WriterequestAnimationFrame(function() {    element2.style.height = (h2 * 2) + 'px';});

      可以很清楚的观察到Animation Frame触发的时机,MDN上说是在paint之前触发,不过我估计是在js脚本交出控制权给浏览器进行DOM的invalidated check之前执行。

      其他注意点

      除了由于触发了layout而导致性能问题外,这边再列出一些其他细节:

      缓存选择器的结果,减少DOM查询。这里要特别提下HTMLCollection。HTMLCollection是通过document.getElementByTagName得到的对象类型,和数组类型很类似但是每次获取这个对象的一个属性,都相当于进行一次DOM查询:

    var divs = document.getElementsByTagName("div");  for (var i = 0; i < divs.length; i++){  //infinite loop    document.body.appendChild(document.createElement("div"));}

      比如上面的这段代码会导致无限循环,所以处理HTMLCollection对象的时候要做些缓存。

      另外,减少DOM元素的嵌套深度并优化css,去除无用的样式对减少layout的计算量有一定帮助。

      在DOM查询时,querySelector和querySelectorAll应该是最后的选择,它们功能最强大,但执行效率很差,如果可以的话,尽量用其他方法替代。

      下面两个jsperf的链接,可以对比下性能。

      1)https://jsperf.com/getelementsbyclassname-vs-queryselectorall/162

      2)http://jsperf.com/getelementbyid-vs-queryselector/218

      自己对View层的想法

      上面的内容理论方面的东西偏多,从实践的角度来看,上面讨论的内容,正好是View层需要处理的事情。已经有一个库FastDOM来做这个事情,不过它的代码是这样的:

    fastdom.read(function() {    console.log('read');});fastdom.write(function() {    console.log('write');});

      问题很明显,会导致callback hell,并且也可以预见到像FastDOM这样的imperative的代码缺乏扩展性,关键在于用了requestAnimationFrame后就变成了异步编程的问题了。要让读写状态同步,那必然需要在DOM的基础上写个Wrapper来内部控制异步读写,不过都到了这份上,感觉可以考虑直接上React了……

      总之,尽量注意避免上面说到的问题,但如果用库,比如jQuery的话,layout的问题出在库本身的抽象上。像React引入自己的组件模型,用过virtual DOM来减少DOM操作,并可以在每次state改变时仅有一次layout,我不知道内部有没有用requestAnimationFrame之类的,感觉要做好一个View层就挺有难度的,之后准备学学React的代码。希望自己一两年后会过来再看这个问题的时候,可以有些新的见解。

      参考

  • http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/

  • https://dev.opera.com/articles/efficient-javascript/?page=3

  • http://wilsonpage.co.uk/preventing-layout-thrashing/

  • https://www.nczonline.net/blog/2009/02/03/speed-up-your-javascript-part-4/

  • http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html

  • 声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
    テキストからウェブサイトへ:HTMLの力テキストからウェブサイトへ:HTMLの力Apr 13, 2025 am 12:07 AM

    HTMLは、Webページを構築するために使用される言語であり、タグと属性を使用してWebページの構造とコンテンツを定義します。 1)htmlは、などのタグを介してドキュメント構造を整理します。 2)ブラウザはHTMLを分析してDOMを構築し、Webページをレンダリングします。 3)マルチメディア関数を強化するなど、HTML5の新機能。 4)一般的なエラーには、閉じられていないラベルと引用されていない属性値が含まれます。 5)最適化の提案には、セマンティックタグの使用とファイルサイズの削減が含まれます。

    HTML、CSS、およびJavaScriptの理解:初心者向けガイドHTML、CSS、およびJavaScriptの理解:初心者向けガイドApr 12, 2025 am 12:02 AM

    webdevelopmentReliesOnhtml、css、andjavascript:1)htmlStructuresContent、2)cssStylesit、および3)Javascriptaddsinteractivity、形成、

    HTMLの役割:Webコンテンツの構造HTMLの役割:Webコンテンツの構造Apr 11, 2025 am 12:12 AM

    HTMLの役割は、タグと属性を使用してWebページの構造とコンテンツを定義することです。 1。HTMLは、読みやすく理解しやすいようなタグを介してコンテンツを整理します。 2。アクセシビリティとSEOを強化するには、セマンティックタグなどを使用します。 3. HTMLコードの最適化により、Webページの読み込み速度とユーザーエクスペリエンスが向上する可能性があります。

    HTMLとコード:用語を詳しく見るHTMLとコード:用語を詳しく見るApr 10, 2025 am 09:28 AM

    htmlisaspecifictypeofcodefocuseduructuringwebcontent

    HTML、CSS、およびJavaScript:Web開発者に不可欠なツールHTML、CSS、およびJavaScript:Web開発者に不可欠なツールApr 09, 2025 am 12:12 AM

    HTML、CSS、およびJavaScriptは、Web開発の3つの柱です。 1。HTMLは、Webページ構造を定義し、などなどのタグを使用します。2。CSSは、色、フォントサイズなどのセレクターと属性を使用してWebページスタイルを制御します。

    HTML、CSS、およびJavaScriptの役割:コアの責任HTML、CSS、およびJavaScriptの役割:コアの責任Apr 08, 2025 pm 07:05 PM

    HTMLはWeb構造を定義し、CSSはスタイルとレイアウトを担当し、JavaScriptは動的な相互作用を提供します。 3人はWeb開発で職務を遂行し、共同でカラフルなWebサイトを構築します。

    HTMLは初心者のために簡単に学ぶことができますか?HTMLは初心者のために簡単に学ぶことができますか?Apr 07, 2025 am 12:11 AM

    HTMLは、簡単に学習しやすく、結果をすばやく見ることができるため、初心者に適しています。 1)HTMLの学習曲線はスムーズで簡単に開始できます。 2)基本タグをマスターして、Webページの作成を開始します。 3)柔軟性が高く、CSSおよびJavaScriptと組み合わせて使用​​できます。 4)豊富な学習リソースと最新のツールは、学習プロセスをサポートしています。

    HTMLでの開始タグの例は何ですか?HTMLでの開始タグの例は何ですか?Apr 06, 2025 am 12:04 AM

    Anexampleapalofastartingtaginhtmlis、それはaperginsaparagraph.startingtagsaresentionentientiontheyinitiateelements、definetheirtypes、およびarecrucialforurturingwebpagesandcontingthomedomを構築します。

    See all articles

    ホットAIツール

    Undresser.AI Undress

    Undresser.AI Undress

    リアルなヌード写真を作成する AI 搭載アプリ

    AI Clothes Remover

    AI Clothes Remover

    写真から衣服を削除するオンライン AI ツール。

    Undress AI Tool

    Undress AI Tool

    脱衣画像を無料で

    Clothoff.io

    Clothoff.io

    AI衣類リムーバー

    AI Hentai Generator

    AI Hentai Generator

    AIヘンタイを無料で生成します。

    ホットツール

    MantisBT

    MantisBT

    Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

    メモ帳++7.3.1

    メモ帳++7.3.1

    使いやすく無料のコードエディター

    MinGW - Minimalist GNU for Windows

    MinGW - Minimalist GNU for Windows

    このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

    PhpStorm Mac バージョン

    PhpStorm Mac バージョン

    最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

    SublimeText3 中国語版

    SublimeText3 中国語版

    中国語版、とても使いやすい