Webの基礎から転載
オブジェクトモデルの構築
ブラウザがページをレンダリングする前に、まずDOMとCSSOMツリーを構築する必要があります。したがって、HTML と CSS の両方ができるだけ早くブラウザーに提供されるようにする必要があります。
バイト→キャラクター→タグ→ノード→オブジェクトモデル。
HTML マークアップはドキュメント オブジェクト モデル (DOM) に変換され、CSS マークアップは CSS オブジェクト モデル (CSSOM) に変換されます。 DOM と CSSOM は独立したデータ構造です。
Chrome DevTools タイムラインは、DOM と CSSOM の構築と処理のオーバーヘッドをキャプチャして検査できます。
ドキュメント オブジェクト モデル (DOM)
<meta><link><title>Critical Path</title> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div>
テキストと画像を含む通常の HTML ページ、ブラウザはこのページをどのように処理しますか?
HTML パーサーによって出力されるツリーは、DOM 要素と属性ノードで構成され、HTML ドキュメントのオブジェクト記述であり、HTML 要素と外部世界 (JavaScript など) の間のインターフェイスでもあります。 DOM とタグはほぼ 1 対 1 で対応します。
変換: ブラウザは、ディスクまたはネットワークから HTML の生のバイトを読み取り、ファイルの指定されたエンコーディング (UTF-8 など) に従って個々の文字に変換します。
-
トークン化: ブラウザは、文字列を、「」、「
」、山かっこ内のその他の文字列など、W3C HTML5 標準で指定されているさまざまなトークンに変換します。各トークンには特別な意味と一連のルールがあります。 字句解析: 発行されたトークンは、そのプロパティとルールを定義する「オブジェクト」に変換されます。
DOM Build: 最後に、HTML タグは異なるタグ間の関係を定義するため (一部のタグは他のタグ内に含まれます)、作成されたオブジェクトは、元の親子関係もキャプチャするツリー データ構造内でリンクされます。マークアップで定義: HTML オブジェクトは body オブジェクトの親、body は paragraph オブジェクトの親など。
プロセス全体の最終出力は、ページのドキュメント オブジェクト モデル (DOM) であり、これはページの以降のすべての処理でブラウザによって使用されます。
ブラウザは HTML マークアップを処理するたびに、バイトを文字に変換し、トークンを決定し、トークンをノードに変換して、DOM ツリーを構築する上記の手順をすべて完了します。特に処理する HTML が大量にある場合、このプロセス全体が完了するまでに時間がかかることがあります。
Chrome DevTools を開いてページの読み込み時のタイムラインを記録すると、このステップの実行に実際にどれくらい時間がかかるかを確認できます。上の例では、大量の HTML バイトを DOM ツリーに変換するのに約 5 ミリ秒かかります。ページが大きい場合、このプロセスに必要な時間が大幅に長くなる可能性があります。スムーズなアニメーションを作成する場合、ブラウザが大量の HTML を処理する必要がある場合、これがボトルネックになりやすいです。
DOM ツリーはドキュメント タグのプロパティと関係をキャプチャしますが、レンダリング時に要素がどのように見えるかは教えてくれません。それは CSSOM の責任です。
CSS オブジェクト モデル (CSSOM)
この単純なページの DOM を構築する過程で、ブラウザーは、外部 CSS スタイル シート style.css を参照するドキュメントの先頭にリンク タグを検出しました。ページをレンダリングするためにリソースが必要になることを予期して、即座にリソースに対してリクエストを行い、以下を返します:
body { font-size: 16px }p { font-weight: bold }span { color: red }p span { display: none }img { float: right }
HTML マークアップ内 (インライン) でスタイルを直接宣言することもできましたが、CSS を HTML から独立させることで、コンテンツとデザインを独立した関心事として扱うことができます。デザイナーは CSS を扱い、開発者は HTML に集中するなどです。
HTML を扱うときと同じように、受け取った CSS ルールをブラウザーが理解して処理できるものに変換する必要があります。したがって、HTML プロセスを繰り返しますが、HTML ではなく CSS の場合:
CSS バイトは文字に変換され、次にトークンとノードに変換され、最後に「CSS オブジェクト モデル」(CSSOM) と呼ばれるものにリンクされます。ツリー構造:
CSSOM はなぜツリー構造なのでしょうか?ページ上のノード オブジェクトのスタイルの最終セットを計算するとき、ブラウザは、ノードに適用される最も一般的なルールから開始し (たとえば、ノードが body 要素の子の場合、すべての body スタイルを適用します)、次にパス より具体的なルールを適用して、計算のスタイルを再帰的に最適化します。
より具体的に説明するために、上記の CSSOM ツリーを例として取り上げます。 body 要素内の span タグ内に配置されたテキストは、16 ピクセルのフォント サイズと赤色になります。フォント サイズ ディレクティブは、本文からスパンまでカスケードされます。ただし、span タグが段落 (p) タグの子である場合、その内容は表示されません。
また、上記のツリーは完全な CSSOM ツリーではなく、スタイルシートでオーバーライドすることを決定したスタイルのみを示していることに注意してください。すべてのブラウザーにはデフォルト スタイル (「ユーザー エージェント スタイル」とも呼ばれます) のセットが提供されており、これが当社のスタイルです。これらのデフォルトのスタイルをオーバーライドするだけです。
CSS 処理にかかる時間を理解するには、DevTools でタイムラインを記録し、「スタイルの再計算」イベントを探します。DOM 解析とは異なり、タイムラインには個別の「CSS 解析」エントリは表示されず、代わりに解析と CSSOM ツリーがキャプチャされます。
この小さなスタイルシートの処理には約 0.6 ミリ秒かかり、ページ上の 8 つの要素に影響を及ぼします。それほど多くはありませんが、それでもオーバーヘッドが発生します。しかし、これらの 8 つの要素はどこから来たのでしょうか? DOM と CSSOM を結び付けるのはレンダー ツリーです。
レンダリング ツリーの構築、レイアウト、描画
CSSOM ツリーと DOM ツリーはレンダリング ツリーにマージされ、これを使用して各表示要素のレイアウトが計算され、描画プロセスに出力されてピクセルが画面にレンダリングされます。 。最適なレンダリング パフォーマンスを達成するには、これらの各ステップを最適化することが重要です。
ブラウザは、HTML と CSS の入力に基づいて DOM ツリーと CSSOM ツリーを構築します。 ただし、これらは完全に独立したオブジェクトであり、ドキュメントのさまざまな側面をキャプチャします。1 つはコンテンツを記述し、もう 1 つはドキュメントに適用する必要があるスタイル ルールを記述します。この 2 つをマージしてブラウザに画面上のピクセルをレンダリングさせるにはどうすればよいでしょうか?
DOM ツリーは CSSOM ツリーとマージされて、Web ページのレンダリングに必要なノードのみを含むレンダリング ツリーを形成します。各 DOM ツリー内のノードを走査し、CSSOM ルール ツリー内の現在のノードのスタイルを見つけて、レンダリング ツリーを生成します。
Layout は、各オブジェクトの正確な位置とサイズを計算します。
最後のステップは描画であり、最終レンダー ツリーを使用してピクセルを画面にレンダリングします。
最初のステップは、ブラウザーに DOM と CSSOM を「レンダリング ツリー」にマージさせ、Web ページ上のすべての 表示 DOM コンテンツ とすべての CSSOM スタイル情報をカバーするようにすることです。各ノードの 。
レンダリング ツリーを構築するために、ブラウザは通常、次の作業を完了します:
DOM ツリーのルート ノードから開始して、表示されている各ノードをトラバースします。
一部のノード (スクリプト タグ、メタ タグなど) は表示されず、レンダリングされた出力に反映されないため無視されます。
一部のノードは CSS によって非表示になっているため、レンダー ツリーでは無視されます。たとえば、「display: none」属性はスパン ノードに設定されているため、レンダリング ツリーには表示されません。
遍历每个可见节点,为其找到适配的 CSSOM 规则并应用它们。从选择器的右边往左边开始匹配,也就是从CSSOM树的子节点开始往父节点匹配。
Emit visible nodes with content and their computed styles.
注: visibility: hidden 与 display: none 是不一样的。前者隐藏元素,但元素仍占据着布局空间(即将其渲染成一个空框),而后者 (display: none) 将元素从渲染树中完全移除,元素既不可见,也不是布局的组成部分。
最终输出的渲染同时包含了屏幕上的所有可见内容及其样式信息。有了渲染树,我们就可以进入“布局”阶段。
到目前为止,我们计算了哪些节点应该是可见的以及它们的计算样式,但我们尚未计算它们在设备视口内的确切位置和大小---这就是“布局”阶段,也称为“reflow”。
为弄清每个对象在网页上的确切大小和位置,浏览器从渲染树的根节点开始进行遍历。让我们考虑一个简单的实例:
<meta><title>Critial Path: Hello world!</title> <div> <div>Hello world!</div> </div>
上記の Web ページの本文には 2 つのネストされた div が含まれています。最初の (親) div はノードの表示サイズをビューポートの幅の 50% に設定し、親 div には幅の 50% の 2 番目の div が含まれています。親、これはビューポート幅の 25% です。
レイアウト プロセスの出力は、ビューポート内の各要素の正確な位置とサイズを正確にキャプチャする「ボックス モデル」です。すべての相対測定値は画面上の絶対ピクセルに変換されます。
最後に、どのノードが表示されているか、その計算されたスタイル、およびジオメトリ情報がわかったので、この情報を最終段階、つまりレンダー ツリー内の各ノードを画面上の実際のピクセルに変換する段階に渡すことができます。このステップは、多くの場合、「ペイント」または「ラスタライズ」と呼ばれます。
Chrome DevTools は、上記の 3 つの段階すべてで消費される時間を深く理解するのに役立ちます。元の「hello world」例のレイアウト フェーズを見てみましょう:
「Layout」イベントは、タイムラインでのレンダー ツリーの構築、位置、およびサイズの計算をキャプチャします。
レイアウトが完了すると、ブラウザは、レンダー ツリーを画面上のピクセルに変換する「ペイント セットアップ」イベントと「ペイント」イベントを発行します。
レンダー ツリーの構築、レイアウト、ペイントの実行に必要な時間は、ドキュメントのサイズ、適用されるスタイル、および設定によって異なります。ドキュメントが実行されているデバイス: ドキュメントが大きくなるほど、ブラウザが実行しなければならない作業が増え、スタイルが複雑になり、描画に時間がかかります (たとえば、単一の色の描画は「小さく」なります。シャドウは多くの計算とレンダリングに「はるかにコストがかかります」)。
ブラウザによって実行される手順の簡単な概要を次に示します:
HTML マークアップを処理し、DOM ツリーを構築します。
CSS マークアップを処理し、CSSOM ツリーを構築します。
DOM と CSSOM をレンダー ツリーにマージします。
レンダリングツリーに従ってレイアウトし、各ノードの幾何情報を計算します。
各ノードを画面に描画します。
DOM または CSSOM が変更された場合は、画面上でどのピクセルを再レンダリングする必要があるかを決定するために、上記の手順をすべて再度実行する必要があります。
クリティカル レンダリング パスの最適化は、上記のシーケンスのステップ 1 から 5 の実行にかかる合計時間を最小限に抑えるプロセスです。これにより、コンテンツができるだけ早く画面にレンダリングされ、その間の時間も短縮されます。最初のレンダリング後に画面が更新されます。つまり、インタラクティブなコンテンツのリフレッシュ レートが向上します。
レンダリングをブロックする CSS デフォルトでは、CSS はレンダリングをブロックするリソースとして扱われます (ただし、 HTML の解析はブロックされません)。 )、これは、CSSOM が構築されるまで、ブラウザーは処理されたコンテンツをレンダリングしないことを意味します。 CSS を縮小し、できるだけ早く配信し、メディア タイプとクエリを活用してレンダリングのブロックを解除して、スクロールせずに見える範囲での時間を短縮してください。
レンダリング ツリーの構築では、DOM と CSSOM の両方がレンダリング ツリーの構築に必要です。これはパフォーマンスに重大な影響を与える可能性があります。HTML と CSS はどちらもレンダリングをブロックするリソースです。 DOM がなければレンダリングするものが何もないため、HTML は明らかに必要ですが、CSS の必要性はそれほど明白ではないかもしれません。 CSS レンダリングのブロックを行わずに通常の Web ページをレンダリングしようとするとどうなるでしょうか?
- デフォルトでは、CSS はレンダリングをブロックするリソースとみなされます。
- メディア タイプとメディア クエリを通じて、一部の CSS リソースを非レンダリング ブロッキングとしてマークできます。
- ブラウザは、ブロックしているかどうかに関係なく、すべての CSS リソースをダウンロードします。
CSS はレンダリングをブロックするリソースです。最初のレンダリング時間を短縮するには、できるだけ早くクライアントにダウンロードする必要があります。
Web ページを表示するときや大型モニターに投影するときなど、特定の条件下でのみ使用される CSS スタイルがある場合はどうすればよいでしょうか?これらのリソースがレンダリングをブロックしなければ良いのですが。 そのような状況は、CSS の「メディア タイプ」と「メディア クエリ」によって解決できます。媒体查询由媒体类型以及零个或多个检查特定媒体特征状况的表达式组成。例如,第一个样式表声明未提供任何媒体类型或查询,因此它适用于所有情况。也就是说它始终会阻塞渲染。第二个样式表则不然,它只在打印内容时适用---或许您想重新安排布局、更改字体等等,因此在网页首次加载时,该样式表不需要阻塞渲染。最后一个样式表声明提供了由浏览器执行的“媒体查询”:符合条件时,样式表会生效,浏览器将阻塞渲染,直至样式表下载并处理完毕。
通过使用媒体查询,我们可以根据特定用例(比如显示或打印),也可以根据动态情况(比如屏幕方向变化、尺寸调整事件等)定制外观。声明样式表时,请密切注意媒体类型和查询,因为它们将严重影响关键渲染路径的性能。
让我们考虑下面这些实例:
第一个声明阻塞渲染,适用于所有情况。
第二个声明同样阻塞渲染:“all”是默认类型,和第一个声明实际上是等效的。
第三个声明具有动态媒体查询,将在网页加载时计算。根据网页加载时设备的方向,portrait.css 可能阻塞渲染,也可能不阻塞渲染。
最后一个声明只在打印网页时应用,因此网页在浏览器中加载时,不会阻塞渲染。
最后,“阻塞渲染”仅是指浏览器是否需要暂停网页的首次渲染,直至该资源准备就绪。无论媒寻是否命中,浏览器都会下载上述所有的CSS样式表,只不过不阻塞渲染的资源对当前媒体不生效罢了。
使用 JavaScript 添加交互
JavaScript 允许我们修改网页的方方面面:内容、样式以及它如何响应用户交互。不过,JavaScript 也会阻止 DOM 构建和延缓网页渲染。为了实现最佳性能,可以让 JavaScript 异步执行,并去除关键渲染路径中任何不必要的 JavaScript。
JavaScript 可以查询和修改 DOM 与 CSSOM。
JavaScript的 执行会阻止 CSSOM的构建,所以和CSSOM的构建是互斥的。
JavaScript blocks DOM construction unless explicitly declared as async.
JavaScript 是一种运行在浏览器中的动态语言,它允许对网页行为的几乎每一个方面进行修改:可以通过在 DOM 树中添加和移除元素来修改内容;可以修改每个元素的 CSSOM 属性;可以处理用户输入等等。为进行说明,让我们用一个简单的内联脚本对之前的“Hello World”示例进行扩展:
<meta><link><title>Critical Path: Script</title><style> body { font-size: 16px };p { font-weight: bold }; span { color: red };p span { display: none }; img { float: right }</style> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div><script> var span = document.getElementsByTagName('span')[0]; span.textContent = 'interactive'; // change DOM text content span.style.display = 'inline'; // change CSSOM property // create a new element, style it, and append it to the DOM var loadTime = document.createElement('div'); loadTime.textContent = 'You loaded this page on: ' + new Date(); loadTime.style.color = 'blue'; document.body.appendChild(loadTime);</script>
JavaScript 允许我们进入 DOM 并获取对隐藏的 span 节点的引用 -- 该节点可能未出现在渲染树中,却仍然存在于 DOM 内。然后,在获得引用后,就可以更改其文本,并将 display 样式属性从“none”替换为“inline”。现在,页面显示“Hello interactive students!”。
JavaScript 还允许我们在 DOM 中创建、样式化、追加和移除新元素。从技术上讲,整个页面可以是一个大的 JavaScript 文件,此文件逐一创建元素并对其进行样式化。但是在实践中,使用 HTML 和 CSS 要简单得多。
尽管 JavaScript 为我们带来了许多功能,不过也在页面渲染方式和时间方面施加了更多限制。
首先,请注意上例中的内联脚本靠近网页底部。为什么呢?如果我们将脚本移至 span元素前面,就会脚本运行失败,并提示在文档中找不到对任何span 元素的引用 -- 即 getElementsByTagName(‘span') 会返回 null。这透露出一个重要事实:脚本在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标记时,它会暂停构建 DOM,将控制权移交给 JavaScript 引擎;等 JavaScript 引擎运行完毕,浏览器会从中断的地方恢复 DOM 构建。
换言之,我们的脚本块在运行时找不到网页中任何靠后的元素,因为它们尚未被处理!或者说:执行内联脚本会阻止 DOM 构建,也就延缓了首次渲染。
在网页中引入脚本的另一个微妙事实是,它们不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。实际上,示例中就是这么做的:将 span 元素的 display 属性从 none 更改为 inline。最终结果如何?我们现在遇到了race condition(资源竞争)。
如果浏览器尚未完成 CSSOM 的下载和构建,而却想在此时运行脚本,会怎样?答案很简单,对性能不利:浏览器将延迟脚本执行和 DOM 构建,直至其完成 CSSOM 的下载和构建。
简言之,JavaScript 在 DOM、CSSOM 和 JavaScript 执行之间引入了大量新的依赖关系,从而可能导致浏览器在处理以及在屏幕上渲染网页时出现大幅延迟:
脚本在文档中的位置很重要。
当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行。
JavaScript 可以查询和修改 DOM 与 CSSOM。
JavaScript 执行将暂停,直至 CSSOM 就绪。即CSSDOM构建的优先级更高。
“优化关键渲染路径”在很大程度上是指了解和优化 HTML、CSS 和 JavaScript 之间的依赖关系谱。
解析器阻塞与异步 JavaScript
默认情况下,JavaScript 执行会“阻塞解析器”:当浏览器遇到文档中的脚本时,它必须暂停 DOM 构建,将控制权移交给 JavaScript 运行时,让脚本执行完毕,然后再继续构建 DOM。实际上,内联脚本始终会阻止解析器,除非编写额外代码来推迟它们的执行。
通过 script 标签引入的脚本又怎样:
<meta><link><title>Critical Path: Script External</title> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div><script></script>
app.js
var span = document.getElementsByTagName('span')[0]; span.textContent = 'interactive'; // change DOM text contentspan.style.display = 'inline'; // change CSSOM property// create a new element, style it, and append it to the DOMvar loadTime = document.createElement('div'); loadTime.textContent = 'You loaded this page on: ' + new Date(); loadTime.style.color = 'blue'; document.body.appendChild(loadTime);
无论我们使用 <script> 标记还是内联 JavaScript 代码段,两者能够以相同方式工作。 <strong>在两种情况下,浏览器都会先暂停并执行脚本,然后才会处理剩余文档。如果是外部<strong> JavaScript <strong>文件,浏览器必须停下来,等待从磁盘、缓存或远程服务器获取脚本,这就可能给关键渲染路径增加更长的延迟。</script>
默认情况下,所有 JavaScript 都会阻止解析器。由于浏览器不了解脚本计划在页面上执行什么操作,它会作最坏的假设并阻止解析器。向浏览器传递脚本不需要在引用位置执行的信号既可以让浏览器继续构建 DOM,也能够让脚本在就绪后执行。为此,我们可以将脚本标记为异步:
<meta> <link> <title>Critical Path: Script Async</title> <p>Hello <span>web performance</span> students!</p> <div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div> <script></script>
向 script 标记添加异步关键字可以指示浏览器在等待脚本可用期间(仅指下载期间,因为所有脚本的执行都会阻塞解析器)不阻止 DOM 构建,这样可以显著提升性能。
分析关键渲染路径性能
发现和解决关键渲染路径性能瓶颈需要充分了解常见的陷阱。让我们踏上实践之旅,找出常见的性能模式,从而帮助您优化网页。
优化关键渲染路径能够让浏览器尽可能快地绘制网页:更快的网页渲染速度可以提高吸引力、增加网页浏览量以及提高转化率。为了最大程度减少访客看到空白屏幕的时间,我们需要优化加载的资源及其加载顺序。
为帮助说明这一流程,让我们先从可能的最简单情况入手,逐步构建我们的网页,使其包含更多资源、样式和应用逻辑。在此过程中,我们还会对每一种情况进行优化,以及了解可能出错的环节。
到目前为止,我们只关注了资源(CSS、JS 或 HTML 文件)可供处理后浏览器中会发生的情况,而忽略了从缓存或从网络获取资源所需的时间。我们作以下假设:
到服务器的网络往返(传播延迟时间)需要 100 毫秒。
HTML 文档的服务器响应时间为 100 毫秒,所有其他文件的服务器响应时间均为 10 毫秒。
Hello World 体验
<meta><title>Critical Path: No Style</title> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div>
我们将从基本 HTML 标记和单个图像(无 CSS 或 JavaScript)开始。让我们在 Chrome DevTools 中打开 Network 时间线并检查生成的资源瀑布:
正如预期的一样,HTML 文件下载花费了大约 200 毫秒。请注意,蓝线的透明部分表示浏览器在网络上等待(即尚未收到任何响应字节)的时间,而不透明部分表示的是收到第一批响应字节后完成下载的时间。HTML 下载量很小 (
当 HTML 内容可用后,浏览器会解析字节,将它们转换成tokens,然后构建 DOM 树。请注意,为方便起见,DevTools 会在底部记录 DOMContentLoaded 事件的时间(216 毫秒),该时间同样与蓝色垂直线相符。HTML 下载结束与蓝色垂直线 (DOMContentLoaded) 之间的间隔是浏览器构建 DOM 树所花费的时间 — 在本例中仅为几毫秒。
请注意,我们的“趣照”并未阻止 domContentLoaded 事件。这证明,我们构建渲染树甚至绘制网页时无需等待页面上的每个静态资源:并非所有资源都对快速提供首次绘制具有关键作用。事实上,当我们谈论关键渲染路径时,通常谈论的是 HTML 标记、CSS 和 JavaScript。图像不会阻止页面的首次渲染,不过,我们当然也应该尽力确保系统尽快绘制图像!
That said, the load event (also known as onload), is blocked on the image: DevTools reports the onload event at 335ms. Recall that the onload event marks the point at which all resources that the page requires have been downloaded and processed; at this point (the red vertical line in the waterfall), the loading spinner can stop spinning in the browser.
结合使用 JavaScript 和 CSS
“Hello World experience”页面虽然看起来简单,但背后却需要做很多工作。在实践中,我们还需要 HTML 之外的其他资源:我们可能需要 CSS 样式表以及一个或多个用于为网页增加一定交互性的脚本。让我们将两者结合使用,看看效果如何:
<title>Critical Path: Measure Script</title><meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div><script></script>
添加 JavaScript 和 CSS 之前:
添加 JavaScript 和 CSS 之后:
添加外部 CSS 和 JavaScript 文件将额外增加两个瀑布请求,浏览器差不多会同时发出这两个请求。不过,请注意,现在 domContentLoaded 事件与 onload 事件之间的时间差小多了。这是怎么回事?
与纯 HTML 示例不同,我们还需要获取并解析 CSS 文件才能构建 CSSOM,要想构建渲染树,DOM 和 CSSOM 缺一不可。
由于网页上还有一个阻塞解析器的JavaScript 文件,系统会在下载并解析 CSS 文件之前阻止 domContentLoaded事件:因为 JavaScript 可能会查询 CSSOM,必须在下载 CSS 文件之后才能执行 JavaScript。
如果我们用内联脚本替换外部脚本会怎样?即使直接将脚本内联到网页中,浏览器仍然无法在构建 CSSOM 之前执行脚本。简言之,内联 JavaScript 也会阻止解析器。
不过,尽管内联脚本会阻止 CSS,但这样做是否能加快页面渲染速度呢?让我们尝试一下,看看会发生什么。
外部 JavaScript:
内联 JavaScript:
我们减少了一个请求,但 onload 和 domContentLoaded 时间实际上没有变化。为什么呢?怎么说呢,我们知道,这与 JavaScript 是内联的还是外部的并无关系,因为只要浏览器遇到 script 标记,就会进行阻止,并等到之前的css文件的 CSSOM 构建完毕。此外,在我们的第一个示例中,浏览器是并行下载 CSS 和 JavaScript,并且差不多是同时完成。在此实例中,内联 JavaScript 代码并无多大意义。但是,我们可以通过多种策略加快网页的渲染速度。
首先回想一下,所有内联脚本都会阻止解析器,但对于外部脚本,可以添加“async”关键字来解除对解析器的阻止。让我们撤消内联,尝试一下这种方法:
<title>Critical Path: Measure Async</title><meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div><script></script>
阻止解析器的(外部)JavaScript:
异步(外部)JavaScript:
效果好多了!解析 HTML 之后不久即会触发 domContentLoaded 事件;浏览器已得知不要阻止 JavaScript,并且由于没有其他阻止解析器的脚本,CSSOM 构建也可并行进行了。
或者,我们也可以同时内联 CSS 和 JavaScript:
<title>Critical Path: Measure Inlined</title><meta><style> p { font-weight: bold } span { color: red } p span { display: none } img { float: right }</style> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div><script> var span = document.getElementsByTagName('span')[0]; span.textContent = 'interactive'; // change DOM text content span.style.display = 'inline'; // change CSSOM property // create a new element, style it, and append it to the DOM var loadTime = document.createElement('div'); loadTime.textContent = 'You loaded this page on: ' + new Date(); loadTime.style.color = 'blue'; document.body.appendChild(loadTime);</script>
请注意,domContentLoaded 时间与前一示例中的时间实际上相同;只不过没有将 JavaScript 标记为异步,而是同时将 CSS 和 JS 内联到网页本身。这会使 HTML 页面显著增大,但好处是浏览器无需等待获取任何外部资源,网页已经内置了所有资源。
即便是非常简单的网页,优化关键渲染路径也并非轻而易举:需要了解不同资源之间的依赖关系图,需要确定哪些资源是“关键资源”,还必须在不同策略中做出选择,找到在网页上加入这些资源的恰当方式。这一问题不是一个解决方案能够解决的,每个页面都不尽相同。您需要遵循相似的流程,自行找到最佳策略。
不过,我们可以回过头来,看看能否找出某些常规性能模式。
性能模式
最简单的网页只包括 HTML 标记;没有 CSS,没有 JavaScript,也没有其他类型的资源。要渲染此类网页,浏览器需要发起请求,等待 HTML 文档到达,对其进行解析,构建 DOM,最后将其渲染在屏幕上:
<meta><title>Critical Path: No Style</title> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div>
T0 与 T1 之间的时间捕获的是网络和服务器处理时间。在最理想的情况下(如果 HTML 文件较小),我们只需一次网络往返便可获取整个文档。由于 TCP 传输协议工作方式的缘故,较大文件可能需要更多次的往返。因此,在最理想的情况下,上述网页具有单次往返(最少)关键渲染路径。
现在,我们还以同一网页为例,但这次使用外部 CSS 文件:
<meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div>
我们同样需要一次网络往返来获取 HTML 文档,然后检索到的标记告诉我们还需要 CSS 文件;这意味着,浏览器需要返回服务器并获取 CSS,然后才能在屏幕上渲染网页。因此,这个页面至少需要两次往返才能显示出来。CSS 文件同样可能需要多次往返,因此重点在于“最少”。
让我们定义一下用来描述关键渲染路径的词汇:
关键资源: 可能阻止网页首次渲染的资源。
关键路径长度: 获取所有关键资源所需的往返次数或总时间。
关键字节: 实现网页首次渲染所需的总字节数,它是所有关键资源传送文件大小的总和。我们包含单个 HTML 页面的第一个示例包含一项关键资源(HTML 文档);关键路径长度也与 1 次网络往返相等(假设文件较小),而总关键字节数正好是 HTML 文档本身的传送大小。
现在,让我们将其与上面 HTML + CSS 示例的关键路径特性对比一下:
2 项关键资源
2 次或更多次往返的最短关键路径长度
9 KB 的关键字节
我们同时需要 HTML 和 CSS 来构建渲染树。所以,HTML 和 CSS 都是关键资源:CSS 仅在浏览器获取 HTML 文档后才会获取,因此关键路径长度至少为两次往返。两项资源相加共计 9KB 的关键字节。
现在,让我们向组合内额外添加一个 JavaScript 文件。
<meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div><script></script>
我们添加了 app.js,它既是网页上的外部 JavaScript 静态资源,又是一种解析器阻止(即关键)资源。更糟糕的是,为了执行 JavaScript 文件,我们还需要进行阻塞并等待 CSSOM;因为JavaScript 可以查询 CSSOM,因此在下载 style.css 并构建 CSSOM 之前,浏览器将会暂停解析。
即便如此,如果我们实际查看一下该网页的“网络瀑布”,就会注意到 CSS 和 JavaScript 请求差不多是同时发起的;浏览器获取 HTML,发现两项资源并发起两个请求。因此,上述网页具有以下关键路径特性:
3 项关键资源
2 次或更多次往返的最短关键路径长度
11 KB 的关键字节
现在,我们拥有了三项关键资源,关键字节总计达 11 KB,但我们的关键路径长度仍是两次往返,因为我们可以同时传送 CSS 和 JavaScript。了解关键渲染路径的特性意味着能够确定哪些是关键资源,此外还能了解浏览器如何安排资源的获取时间。让我们继续探讨示例。
在与网站开发者交流后,我们意识到我们在网页上加入的 JavaScript 不必具有阻塞作用:网页中的一些分析代码和其他代码不需要阻止网页的渲染。了解了这一点,我们就可以向 script 标记添加“async”属性来解除对解析器的阻止:
<meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div><script></script>
异步脚本具有以下几个优点:
脚本不再阻止解析器,也不再是关键渲染路径的组成部分。
由于没有其他关键脚本,CSS 也不需要阻止 domContentLoaded 事件。
domContentLoaded 事件触发得越早,其他应用逻辑开始执行的时间就越早。
因此,我们优化过的网页现在恢复到了具有两项关键资源(HTML 和 CSS),最短关键路径长度为两次往返,总关键字节数为 9 KB。
最后,如果 CSS 样式表只需用于打印,那会如何呢?
<meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="ブラウザでページをレンダリングする方法をまとめます" ></div><script></script>
style.css リソースは印刷にのみ使用されるため、ブラウザーは Web ページをレンダリングするためにそれをブロックする必要はありません。したがって、DOM が構築されると、ブラウザーには Web ページをレンダリングするのに十分な情報が含まれます。したがって、ページにはクリティカル リソース (HTML ドキュメント) が 1 つだけあり、クリティカル レンダリング パスの最短の長さは 1 往復です。
概要:
デフォルトでは、CSS はレンダリング ブロック リソースとして扱われます。これは、CSSOM が構築されるまで、ブラウザーは処理されたコンテンツをレンダリングしないことを意味します。 DOM と CSSDOM をできるだけ早く構築することによってのみ、最初の画面をできるだけ早く表示することができます。ただし、CSS 解析と HTML 解析は並行して実行できます。 HTML パーサーが script タグに遭遇すると、DOM の構築を一時停止し、js ファイルを (外部/インライン/キャッシュから) ダウンロードしてから、制御を JavaScript エンジンに転送します (スクリプトがこの時点の要素を参照している場合、参照エラーが発生します); JavaScript エンジンの実行が終了すると、ブラウザは中断されたところから DOM の構築を再開します。つまり、ページに script タグがある場合、DOMContentLoaded イベントは、JS が実行されるまで待機してからトリガーされる必要があります。ただし、スクリプトを非同期としてマークすることができます。これにより、js ファイルのダウンロード中に DOM の構築がブロックされなくなります。
defer と async は両方とも js ファイルを非同期的にダウンロードしますが、違いがあります。
defer 属性は IE でのみサポートされており、この属性を持つスクリプトはページが解析された後に実行され、遅延されたスクリプトは必ずしも順番に実行されるわけではありません。 async js はダウンロード後すぐに実行されます (したがって、スクリプトが実行される順序は、コード内のスクリプトの順序ではありません。後から表示されるスクリプトが最初にロードされ、最初に実行される可能性があります)。
非同期リソースはパーサーをブロックしないため、ブラウザーはスクリプトを実行する前に CSSOM の構築でブロックされることを回避できます。一般に、スクリプトで async 属性を使用できる場合、つまり最初のレンダリングには必要ない場合は、最初のレンダリング後にスクリプトを非同期でロードすることを検討してください。
ブラウザがダウンロードを完了していない場合はどうなりますか? スクリプトを実行したいときに CSSOM を構築する 答えは簡単ですが、そうではありません。 パフォーマンスが非常に良い: ブラウザーはスクリプトの実行と DOM を遅延させます。 つまり、script タグ内の JS は、その前にある CSS がロードされるまで待機してから実行する必要があります。
HTML パーサーはどのように DOM ツリーを構築しますか? DOM ツリーと html タグの間には 1 対 1 の対応関係があり、HTML を上から下に解析すると、解析中に DOM が構築されます。外部リソース (リンクまたはスクリプト) が見つかった場合、外部リソースがロードされます。外部リソースが js の場合、HTML 解析は一時停止され、js がロードされて実行されるまで続行されます。外部リソースが css の場合、HTML 解析には影響しませんが、最初の画面のレンダリングには影響します。
domContentLoaded: CSS ファイル、画像、iframe のロードを待たずに、最初の HTML ドキュメントがロードされて DOM ツリーに解析されたときにトリガーされます。 load
: ページに必要なすべてのリソース (画像を含む) がダウンロードされ、処理されたとき。動的に取得されたリソースは、load イベントとは何の関係もありません。
以上がブラウザでページをレンダリングする方法をまとめますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

JavaScriptはブラウザとnode.js環境で実行され、JavaScriptエンジンに依存してコードを解析および実行します。 1)解析段階で抽象的構文ツリー(AST)を生成します。 2)ASTをコンパイル段階のバイトコードまたはマシンコードに変換します。 3)実行段階でコンパイルされたコードを実行します。

PythonとJavaScriptの将来の傾向には、1。Pythonが科学コンピューティングの分野での位置を統合し、AI、2。JavaScriptはWebテクノロジーの開発を促進します。どちらもそれぞれのフィールドでアプリケーションシナリオを拡大し続け、パフォーマンスをより多くのブレークスルーを行います。

開発環境におけるPythonとJavaScriptの両方の選択が重要です。 1)Pythonの開発環境には、Pycharm、Jupyternotebook、Anacondaが含まれます。これらは、データサイエンスと迅速なプロトタイピングに適しています。 2)JavaScriptの開発環境には、フロントエンドおよびバックエンド開発に適したnode.js、vscode、およびwebpackが含まれます。プロジェクトのニーズに応じて適切なツールを選択すると、開発効率とプロジェクトの成功率が向上する可能性があります。

はい、JavaScriptのエンジンコアはCで記述されています。1)C言語は、JavaScriptエンジンの開発に適した効率的なパフォーマンスと基礎となる制御を提供します。 2)V8エンジンを例にとると、そのコアはCで記述され、Cの効率とオブジェクト指向の特性を組み合わせて書かれています。3)JavaScriptエンジンの作業原理には、解析、コンパイル、実行が含まれ、C言語はこれらのプロセスで重要な役割を果たします。

JavaScriptは、Webページのインタラクティブ性とダイナミズムを向上させるため、現代のWebサイトの中心にあります。 1)ページを更新せずにコンテンツを変更できます。2)Domapiを介してWebページを操作する、3)アニメーションやドラッグアンドドロップなどの複雑なインタラクティブ効果、4)ユーザーエクスペリエンスを改善するためのパフォーマンスとベストプラクティスを最適化します。

CおよびJavaScriptは、WebAssemblyを介して相互運用性を実現します。 1)CコードはWebAssemblyモジュールにコンパイルされ、JavaScript環境に導入され、コンピューティングパワーが強化されます。 2)ゲーム開発では、Cは物理エンジンとグラフィックスレンダリングを処理し、JavaScriptはゲームロジックとユーザーインターフェイスを担当します。

JavaScriptは、Webサイト、モバイルアプリケーション、デスクトップアプリケーション、サーバー側のプログラミングで広く使用されています。 1)Webサイト開発では、JavaScriptはHTMLおよびCSSと一緒にDOMを運用して、JQueryやReactなどのフレームワークをサポートします。 2)ReactNativeおよびIonicを通じて、JavaScriptはクロスプラットフォームモバイルアプリケーションを開発するために使用されます。 3)電子フレームワークにより、JavaScriptはデスクトップアプリケーションを構築できます。 4)node.jsを使用すると、JavaScriptがサーバー側で実行され、高い並行リクエストをサポートします。

Pythonはデータサイエンスと自動化により適していますが、JavaScriptはフロントエンドとフルスタックの開発により適しています。 1. Pythonは、データ処理とモデリングのためにNumpyやPandasなどのライブラリを使用して、データサイエンスと機械学習でうまく機能します。 2。Pythonは、自動化とスクリプトにおいて簡潔で効率的です。 3. JavaScriptはフロントエンド開発に不可欠であり、動的なWebページと単一ページアプリケーションの構築に使用されます。 4. JavaScriptは、node.jsを通じてバックエンド開発において役割を果たし、フルスタック開発をサポートします。


ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

WebStorm Mac版
便利なJavaScript開発ツール

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

メモ帳++7.3.1
使いやすく無料のコードエディター

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター

ホットトピック









