Nachdruck aus Web Fundamental
Bevor der Browser die Seite rendert, muss er die DOM- und CSSOM-Bäume erstellen. Daher müssen wir sicherstellen, dass sowohl HTML als auch CSS so schnell wie möglich an den Browser geliefert werden.
Byte → Zeichen → Tag → Knoten → Objektmodell.
HTML-Markup wird in das Document Object Model (DOM) konvertiert; CSS-Markup wird in das CSS Object Model (CSSOM) konvertiert. DOM und CSSOM sind unabhängige Datenstrukturen.
Chrome DevTools Timeline kann den Konstruktions- und Verarbeitungsaufwand von DOM und CSSOM erfassen und überprüfen.
<html> <head><meta name="viewport" content="width=device-width,initial-scale=1"><link href="style.css?1.1.11" rel="stylesheet"><title>Critical Path</title> </head> <body><p>Hello <span>web performance</span> students!</p><div><img src="awesome-photo.jpg"></div> </body></html>
Ein einfaches HTML, das etwas Text und eine Bildseite enthält, Wie verarbeitet der Browser diese Seite?
Die vom HTML-Parser ausgegebene Baumstruktur besteht aus DOM-Elementen und Attributknoten. Sie stellt eine Objektbeschreibung des HTML-Dokuments dar und ist auch die Schnittstelle zwischen HTML-Elementen und der Außenwelt (z. B. Javascript). DOM und Tags haben eine nahezu eins-zu-eins-Entsprechung.
Konvertierung: Der Browser liest die Rohbytes von HTML von der Festplatte oder dem Netzwerk und kodiert sie entsprechend der angegebenen Kodierung der Datei (z. B. UTF-8), um sie in einzelne Zeichen umzuwandeln.
Tokenisierung: Der Browser konvertiert die Zeichenfolge in verschiedene vom W3C-HTML5-Standard angegebene Token, zum Beispiel „“ , „
“ und andere Zeichenfolgen in spitzen Klammern. Jeder Token hat eine besondere Bedeutung und eine Reihe von Regeln.Lexikalische Analyse: Die von ausgegebenen Tags werden in „Objekte“ umgewandelt, die ihre Eigenschaften und Regeln definieren.
DOM Build: Da HTML-Tags schließlich Beziehungen zwischen verschiedenen Tags definieren (einige Tags sind in anderen Tags enthalten), erstellen Sie die Objekte sind innerhalb einer Baumdatenstruktur verknüpft, die auch die im ursprünglichen Markup definierte Eltern-Kind-Beziehung erfasst: Das HTML-Objekt ist das übergeordnete Objekt des body-Objekts, body ist das übergeordnete Element des Absatz-Objekts und so weiter.
Die endgültige Ausgabe des gesamten Prozesses ist das Dokumentobjektmodell (DOM) der Seite und die gesamte weitere Verarbeitung der Seite durch den Browser Jeder wird es nutzen.
Jedes Mal, wenn der Browser ein HTML-Tag verarbeitet, führt er alle oben genannten Schritte aus: Bytes in Zeichen umwandeln, Token bestimmen, Token in Knoten umwandeln und dann den DOM-Baum erstellen. Dieser gesamte Vorgang kann einige Zeit in Anspruch nehmen, insbesondere wenn Sie viel HTML verarbeiten müssen.
Wenn Sie Chrome DevTools öffnen und die Zeitleiste beim Laden der Seite aufzeichnen, können Sie sehen, wie lange es tatsächlich dauert, diesen Schritt auszuführen. Im obigen Beispiel dauert die Konvertierung einer Reihe von HTML-Bytes in einen DOM-Baum etwa 5 Millisekunden. Bei größeren Seiten kann sich die für diesen Vorgang erforderliche Zeit erheblich erhöhen. Bei der Erstellung flüssiger Animationen kann dies leicht zu einem Flaschenhals werden, wenn der Browser viel HTML verarbeiten muss.
Der DOM-Baum erfasst die Eigenschaften und Beziehungen von Dokument-Tags, sagt uns jedoch nicht, wie die Elemente beim Rendern aussehen werden. Das liegt in der Verantwortung von CSSOM.
Während der Browser den DOM dieser einfachen Seite erstellt, wird im Kopf des Dokuments ein Link-Tag angetroffen. was sich auf ein externes CSS-Stylesheet bezieht: style.css. Da davon ausgegangen wird, dass die Ressource zum Rendern der Seite benötigt wird, wird sofort eine Anforderung für die Ressource gestellt und Folgendes zurückgegeben:
body { font-size: 16px }p { font-weight: bold }span { color: red }p span { display: none }img { float: right }
Wir hätten die Stile direkt im HTML-Markup (inline) deklarieren können, aber da CSS unabhängig von HTML ist, können wir Inhalt und Design als unabhängige Anliegen behandeln: Designer kümmern sich um CSS, Entwickler konzentrieren sich auf HTML usw.
Genau wie bei der Arbeit mit HTML müssen wir die empfangenen CSS-Regeln in etwas umwandeln, das der Browser verstehen und verarbeiten kann. Also wiederholen wir den HTML-Prozess, aber für CSS statt HTML:
CSS-Bytes werden in Zeichen umgewandelt, dann in Token und Knoten und schließlich mit einem Namen verknüpft Die Baumstruktur des CSS Object Model (CSSOM):
Warum hat CSSOM eine Baumstruktur? Bei der Berechnung des endgültigen Stilsatzes für ein beliebiges Knotenobjekt auf der Seite beginnt der Browser mit der häufigsten Regel, die für den Knoten gilt (wenn der Knoten beispielsweise ein untergeordnetes Element des Body-Elements ist, wenden Sie alle Body-Stile an) und dann Pässe Wenden Sie spezifischere Regeln an, um den Berechnungsstil rekursiv zu optimieren.
Nehmen Sie den obigen CSSOM-Baum als Beispiel für eine detailliertere Erklärung. Jeder Text, der innerhalb eines span-Tags innerhalb des Body-Elements platziert wird, hat eine Schriftgröße von 16 Pixel und ist rot gefärbt. Font-Size-Direktiven kaskadieren von Body zu Span. Wenn ein Span-Tag jedoch ein untergeordnetes Element eines Absatz-Tags (p) ist, wird sein Inhalt nicht angezeigt.
Beachten Sie außerdem, dass der Baum oben nicht der vollständige CSSOM-Baum ist und nur die Stile zeigt, die wir in unserem Stylesheet überschreiben möchten. Jeder Browser stellt eine Reihe von Standardstilen bereit (auch „User Agent-Stile“ genannt). , das heißt, unsere Stile überschreiben einfach diese Standardstile.
Um zu sehen, wie lange die CSS-Verarbeitung dauert, protokollieren Sie die Zeitleiste in DevTools und suchen Sie nach dem Ereignis „Stil neu berechnen“: Im Gegensatz zur DOM-Analyse zeigt die Zeitleiste keinen separaten Eintrag „CSS analysieren“ an, sondern erfasst stattdessen Parsing und CSSOM-Baumkonstruktion sowie die rekursive Berechnung berechneter Stile unter diesem einen Ereignis – nicht viel, verursacht aber dennoch Overhead. Aber woher kommen diese 8 Elemente? Was DOM und CSSOM miteinander verbindet, ist der Renderbaum.
Aufbau, Layout und Zeichnung des Rendering-Baums
Der CSSOM-Baum und der DOM-Baum werden zu einem Rendering-Baum zusammengeführt, der dann zur Berechnung des Layouts jedes sichtbaren Elements und zur Ausgabe in die Zeichnung verwendet wird Prozess, der die Pixel auf dem Bildschirm rendert. Die Optimierung jedes dieser Schritte ist entscheidend, um eine optimale Rendering-Leistung zu erzielen. Der Browser erstellt DOM-Bäume und CSSOM-Bäume basierend auf HTML- und CSS-Eingaben. Es handelt sich jedoch umDer DOM-Baum wird mit dem CSSOM-Baum zusammengeführt, um einen Rendering-Baum zu bilden, der nur die Knoten enthält, die zum Rendern der Webseite benötigt werden. Durchlaufen Sie die Knoten in jedem DOM-Baum, suchen Sie den Stil des aktuellen Knotens im CSSOM-Regelbaum und generieren Sie einen Rendering-Baum.
abdeckt DOM
Inhalte und alle CSSOM-Stilinformationen für jeden Knoten. Um den Rendering-Baum zu erstellen, führt der Browser im Allgemeinen die folgende Arbeit aus:
Durchlaufen Sie jeden Knoten ausgehend vom Wurzelknoten von der DOM-Baum.Einige Knoten sind nicht sichtbar (z. B. Skript-Tags, Meta-Tags usw.) und werden ignoriert, da sie nicht in der gerenderten Ausgabe widergespiegelt werden.
遍历每个可见节点,为其找到适配的 CSSOM 规则并应用它们。从选择器的右边往左边开始匹配,也就是从CSSOM树的子节点开始往父节点匹配。
Emit visible nodes with content and their computed styles.
注: visibility: hidden 与 display: none 是不一样的。前者隐藏元素,但元素仍占据着布局空间(即将其渲染成一个空框),而后者 (display: none) 将元素从渲染树中完全移除,元素既不可见,也不是布局的组成部分。
最终输出的渲染同时包含了屏幕上的所有可见内容及其样式信息。有了渲染树,我们就可以进入“布局”阶段。
到目前为止,我们计算了哪些节点应该是可见的以及它们的计算样式,但我们尚未计算它们在设备视口内的确切位置和大小---这就是“布局”阶段,也称为“reflow”。
为弄清每个对象在网页上的确切大小和位置,浏览器从渲染树的根节点开始进行遍历。让我们考虑一个简单的实例:
<html> <head><meta name="viewport" content="width=device-width,initial-scale=1"><title>Critial Path: Hello world!</title> </head> <body><div style="width: 50%"> <div style="width: 50%">Hello world!</div></div> </body></html>
Der Hauptteil der obigen Webseite enthält zwei verschachtelte Divs: Das erste (übergeordnete) Div legt die Anzeigegröße des Knotens auf 50 % der Breite des Ansichtsfensters fest, und das übergeordnete Div enthält ein zweites Div mit einer Breite von 50 %. des übergeordneten Elements, was 25 % der Breite des Ansichtsfensters entspricht.
Die Ausgabe des Layoutprozesses ist ein „Boxmodell“, das die genaue Position und Größe jedes Elements im Ansichtsfenster genau erfasst: alle relativ Messungen werden auf dem Bildschirm in absolute Pixel umgewandelt.
Da wir nun endlich wissen, welche Knoten sichtbar sind, ihre berechneten Stile und Geometrieinformationen, können wir diese Informationen endlich an die letzte Stufe weitergeben: die Umwandlung jedes Knotens im Renderbaum in tatsächliche Bildschirmpixel . Dieser Schritt wird oft als „Malen“ oder „Rasterisieren“ bezeichnet.
Chrome DevTools können uns helfen, Erkenntnisse darüber zu gewinnen, wie lange alle drei oben genannten Phasen dauern. Werfen wir einen Blick auf die Layoutphase des ursprünglichen „Hallo Welt“-Beispiels:
Das Ereignis „Layout“ erfasst die Konstruktion, Position und Größenberechnung des Renderbaums im Zeitleiste.
Wenn das Layout abgeschlossen ist, gibt der Browser die Ereignisse „Paint Setup“ und „Paint“ aus, die den Renderbaum in Pixel auf dem Bildschirm umwandeln.
Erforderlich für die Renderbaumkonstruktion. Layout und Zeichnung Die Zeit hängt von der Größe des Dokuments, den angewendeten Stilen und dem Gerät ab, auf dem das Dokument ausgeführt wird: Je größer das Dokument, desto mehr Arbeit muss der Browser leisten, desto länger ist es wird zum Zeichnen benötigt (z. B. Zeichnen einer einzelnen Farbe). Der Overhead ist „kleiner“, während der Berechnungs- und Rendering-Overhead von Schatten „viel größer“ ist.
Hier ist eine kurze Übersicht über die vom Browser ausgeführten Schritte:
Verarbeiten Sie das HTML-Markup und erstellen Sie den DOM-Baum.
CSS-Markup verarbeiten und CSSOM-Baum erstellen.
DOM und CSSOM in einem Renderbaum zusammenführen.
Layout gemäß dem Rendering-Baum, um die geometrischen Informationen jedes Knotens zu berechnen.
Zeichnen Sie jeden Knoten auf den Bildschirm.
Wenn das DOM oder CSSOM geändert wird, müssen alle oben genannten Schritte erneut ausgeführt werden, um zu bestimmen, welche Pixel auf dem Bildschirm neu gerendert werden müssen.
Durch die Optimierung des kritischen Rendering-Pfads wird die Gesamtzeit minimiert, die für die Ausführung der Schritte 1 bis 5 in der obigen Reihenfolge aufgewendet wird. Dadurch werden Inhalte möglichst schnell auf dem Bildschirm gerendert wie möglich und reduziert auch die Zeitspanne zwischen Bildschirmaktualisierungen nach dem ersten Rendern, d eine Ressource, die
das Rendern blockiert), was bedeutet, dass der Browser keine verarbeiteten Inhalte rendert, bis das CSSOM erstellt ist. Achten Sie darauf, Ihr CSS zu minimieren, es so schnell wie möglich bereitzustellen und Medientypen und Abfragen zu nutzen, um die Blockierung des Renderings zu entsperren, um die Zeit oberhalb der Falte zu reduzieren. Beim Rendering-Baumaufbau sind sowohl DOM als auch CSSOM erforderlich, um den Rendering-Baum zu erstellen. Dies kann schwerwiegende Auswirkungen auf die Leistung haben: HTML
undCSS sind beide Rendering-blockierende Ressourcen. HTML ist offensichtlich erforderlich, da es ohne DOM nichts zu rendern gibt, aber die Notwendigkeit von CSS ist möglicherweise weniger offensichtlich. Was passiert, wenn Sie versuchen, eine normale Webseite ohne CSS-Rendering-Blockierung zu rendern? Standardmäßig wird CSS als renderblockierende Ressource behandelt.
Wir können einige CSS-Ressourcen durch Medientypen und Medienabfragen als nicht blockierend markieren.
Der Browser lädt alle CSS-Ressourcen herunter, unabhängig davon, ob sie blockieren oder nicht.
Webseiten ohne CSS sind praktisch unbrauchbar. Daher blockiert der Browser das Rendern, bis sowohl das DOM als auch das CSSOM bereit sind.
Was ist, wenn es einige CSS-Stile gibt, die nur unter bestimmten Bedingungen verwendet werden, beispielsweise wenn die Webseite auf einem großen Monitor angezeigt oder projiziert wird? Es wäre schön, wenn diese Ressourcen das Rendern nicht blockieren würden. Solche Situationen können durch CSS „Medientypen“ und „Medienabfragen“ gelöst werden:
媒体查询由媒体类型以及零个或多个检查特定媒体特征状况的表达式组成。例如,第一个样式表声明未提供任何媒体类型或查询,因此它适用于所有情况。也就是说它始终会阻塞渲染。第二个样式表则不然,它只在打印内容时适用---或许您想重新安排布局、更改字体等等,因此在网页首次加载时,该样式表不需要阻塞渲染。最后一个样式表声明提供了由浏览器执行的“媒体查询”:符合条件时,样式表会生效,浏览器将阻塞渲染,直至样式表下载并处理完毕。
通过使用媒体查询,我们可以根据特定用例(比如显示或打印),也可以根据动态情况(比如屏幕方向变化、尺寸调整事件等)定制外观。声明样式表时,请密切注意媒体类型和查询,因为它们将严重影响关键渲染路径的性能。
让我们考虑下面这些实例:
第一个声明阻塞渲染,适用于所有情况。
第二个声明同样阻塞渲染:“all”是默认类型,和第一个声明实际上是等效的。
第三个声明具有动态媒体查询,将在网页加载时计算。根据网页加载时设备的方向,portrait.css 可能阻塞渲染,也可能不阻塞渲染。
最后一个声明只在打印网页时应用,因此网页在浏览器中加载时,不会阻塞渲染。
最后,“阻塞渲染”仅是指浏览器是否需要暂停网页的首次渲染,直至该资源准备就绪。无论媒寻是否命中,浏览器都会下载上述所有的CSS样式表,只不过不阻塞渲染的资源对当前媒体不生效罢了。
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”示例进行扩展:
<html> <head><meta name="viewport" content="width=device-width,initial-scale=1"><link href="style.css?1.1.11" rel="stylesheet"><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> </head> <body><p>Hello <span>web performance</span> students!</p><div><img src="awesome-photo.jpg"></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> </body></html>
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 执行会“阻塞解析器”:当浏览器遇到文档中的脚本时,它必须暂停 DOM 构建,将控制权移交给 JavaScript 运行时,让脚本执行完毕,然后再继续构建 DOM。实际上,内联脚本始终会阻止解析器,除非编写额外代码来推迟它们的执行。
通过 script 标签引入的脚本又怎样:
<html> <head><meta name="viewport" content="width=device-width,initial-scale=1"><link href="style.css?1.1.11" rel="stylesheet"><title>Critical Path: Script External</title> </head> <body><p>Hello <span>web performance</span> students!</p><div><img src="awesome-photo.jpg"></div><script src="app.js?1.1.11"></script> </body></html>
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);
无论我们使用