Réimprimé à partir des principes fondamentaux du Web
Avant que le navigateur n'affiche la page, il doit créer les arborescences DOM et CSSOM. Par conséquent, nous devons nous assurer que HTML et CSS sont transmis au navigateur le plus rapidement possible.
octet → caractère → balise → nœud → modèle objet.
Le balisage HTML est converti en modèle d'objet de document (DOM) ; le balisage CSS est converti en modèle d'objet CSS (CSSOM). DOM et CSSOM sont des structures de données indépendantes.
Chrome DevTools Timeline peut capturer et inspecter la surcharge de construction et de traitement du DOM et du CSSOM.
<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>
Un HTML simple contenant du texte et une page d'image, comment le navigateur traite-t-il cette page ?
L'arborescence générée par l'analyseur HTML est composée d'éléments DOM et de nœuds d'attribut. Il s'agit d'une description d'objet du document HTML et constitue également l'interface entre les éléments HTML et le monde extérieur (tel que Javascript). Le DOM et les balises ont une correspondance presque individuelle.
Conversion : Le navigateur lit les octets bruts du HTML à partir du disque ou du réseau et les encode selon l'encodage spécifié du fichier. (comme UTF-8) pour les convertir en caractères individuels.
Tokenisation : Le navigateur convertit la chaîne en divers jetons spécifiés par la norme HTML5 du W3C, par exemple "" , "
" et d'autres chaînes entre crochets angulaires. Chaque jeton a une signification particulière et un ensemble de règles.Analyse lexicale : Les balises émises par sont converties en "objets" qui définissent leurs propriétés et règles.
DOM Build : Enfin, puisque les balises HTML définissent des relations entre différentes balises (certaines balises sont contenues dans d'autres balises), créez les objets sont liés au sein d'une structure de données arborescente, qui capture également la relation parent-enfant définie dans le balisage d'origine : l'objet HTML est le parent de l'objet body, body est le parent de l'objet paragraph, et ainsi de suite.
Le résultat final de l'ensemble du processus est le modèle objet de document (DOM) de la page , et tous les traitements ultérieurs de la page par le navigateur Tout le monde l'utilisera.
Chaque fois que le navigateur traite une balise HTML, il effectue toutes les étapes ci-dessus : convertir les octets en caractères, déterminer les jetons, convertir les jetons en nœuds, puis construire l'arborescence DOM. L’ensemble de ce processus peut prendre un certain temps, surtout si vous avez beaucoup de HTML à traiter.
Si vous ouvrez Chrome DevTools et enregistrez la chronologie pendant le chargement de la page, vous pouvez voir combien de temps il faut réellement pour effectuer cette étape. Dans l'exemple ci-dessus, il faut environ 5 millisecondes pour convertir un groupe d'octets HTML en une arborescence DOM. Pour les pages plus volumineuses, le temps requis pour ce processus peut augmenter considérablement. Lors de la création d’animations fluides, cela peut facilement devenir un goulot d’étranglement si le navigateur doit traiter beaucoup de HTML.
L'arborescence DOM capture les propriétés et les relations des balises de document, mais ne nous dit pas à quoi ressembleront les éléments une fois rendus. C'est la responsabilité du CSSOM.
Dans le processus de construction par le navigateur du DOM de cette page simple, une balise de lien est rencontrée dans l'en-tête du document, qui fait référence à une feuille de style CSS externe : style.css. Anticipant que la ressource sera nécessaire pour afficher la page, il fait immédiatement une demande pour la ressource et renvoie ce qui suit :
body { font-size: 16px }p { font-weight: bold }span { color: red }p span { display: none }img { float: right }
Nous aurions pu déclarer les styles directement dans le balisage HTML (inline), mais avoir un CSS indépendant du HTML nous permet de traiter le contenu et le design comme des préoccupations indépendantes : les concepteurs gèrent le CSS, les développeurs se concentrent sur le HTML, etc.
Tout comme lorsque nous travaillons avec HTML, nous devons convertir les règles CSS reçues en quelque chose que le navigateur peut comprendre et traiter. Nous allons donc répéter le processus HTML, mais pour du CSS au lieu du HTML :
Les octets CSS sont convertis en caractères, puis en jetons et nœuds, et enfin liés à un nom La structure arborescente du modèle objet CSS (CSSOM) :
Pourquoi CSSOM a-t-il une structure arborescente ? Lors du calcul de l'ensemble final de styles pour un objet nœud sur la page, le navigateur commence par la règle la plus courante qui s'applique au nœud (par exemple, si le nœud est un enfant de l'élément body, appliquez tous les styles de corps), puis passes Appliquer des règles plus spécifiques pour optimiser de manière récursive le style des calculs.
Prenez l'arbre CSSOM ci-dessus comme exemple pour une explication plus détaillée. Tout texte placé dans une balise span à l'intérieur de l'élément body aura une taille de police de 16 pixels et sera coloré en rouge. Les directives de taille de police descendent du corps à l'étendue. Cependant, si une balise span est un enfant d'une balise paragraphe (p), son contenu ne sera pas affiché.
Notez également que l'arborescence ci-dessus n'est pas l'arborescence CSSOM complète et montre uniquement les styles que nous avons décidé de remplacer dans notre feuille de style. Chaque navigateur fournit un ensemble de styles par défaut (également appelés "Styles d'agent utilisateur" "). , c'est-à-dire que nos styles remplacent simplement ces styles par défaut.
Pour voir combien de temps prend le traitement CSS, enregistrez la chronologie dans DevTools et recherchez l'événement « Recalculer le style » : contrairement à l'analyse DOM, la chronologie n'affiche pas d'entrée « Analyser CSS » distincte, mais capture à la place l'analyse syntaxique et la construction de l'arborescence CSSOM, ainsi que le calcul récursif des styles calculés sous cet événement unique - pas grand-chose, mais entraîne toujours une surcharge. Mais d’où viennent ces 8 éléments ? Ce qui relie le DOM et le CSSOM, c'est l'arbre de rendu.
Construction, disposition et dessin de l'arbre de rendu
L'arbre CSSOM et l'arbre DOM sont fusionnés dans un arbre de rendu, qui est ensuite utilisé pour calculer la disposition de chaque élément visible et l'afficher dans le dessin processus, rendant les pixels sur l’écran. L'optimisation de chacune de ces étapes est essentielle pour obtenir des performances de rendu optimales. Le navigateur crée des arbres DOM et CSSOM basés sur les entrées HTML et CSS. Cependant, ce sont desL'arborescence DOM est fusionnée avec l'arborescence CSSOM pour former une arborescence de rendu, qui contient uniquement les nœuds nécessaires au rendu de la page Web. Parcourez les nœuds de nœud dans chaque arborescence DOM, recherchez le style du nœud actuel dans l'arborescence de règles CSSOM et générez une arborescence de rendu.
DOM
Contenu et toutes les informations de style CSSOM pour chaque nœud. Pour construire l'arbre de rendu, le navigateur effectue généralement le travail suivant :
Parcourir chaque nœud en commençant par le nœud racine de l'arborescence DOM.Certains nœuds ne sont pas visibles (par exemple, les balises de script, les balises méta, etc.) et sont ignorés car ils ne sont pas reflétés dans la sortie rendue.
遍历每个可见节点,为其找到适配的 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>
Le corps de la page Web ci-dessus contient deux div imbriqués : le premier div (parent) définit la taille d'affichage du nœud à 50 % de la largeur de la fenêtre d'affichage, et le div parent contient un deuxième div avec une largeur de 50 % de son parent. %, qui représente 25 % de la largeur de la fenêtre.
Le résultat du processus de mise en page est un « modèle de boîte » qui capture avec précision la position et la taille exactes de chaque élément dans la fenêtre : tous relatifs les mesures sont converties en pixels absolus sur l'écran.
Enfin, maintenant que nous savons quels nœuds sont visibles, leurs styles calculés et les informations géométriques, nous pouvons enfin transmettre ces informations à l'étape finale : convertir chaque nœud de l'arbre de rendu en pixels réels à l'écran. . Cette étape est souvent appelée « peinture » ou « rastérisation ».
Chrome DevTools peut nous aider à mieux comprendre la durée des trois étapes ci-dessus. Jetons un coup d'œil à la phase de mise en page de l'exemple original "hello world" :
L'événement "Layout" capture la construction, la position et le calcul de la taille de l'arbre de rendu dans le Chronologie.
Une fois la mise en page terminée, le navigateur émet les événements "Paint Setup" et "Paint", qui convertissent l'arbre de rendu en pixels à l'écran.
Obligatoire pour effectuer la construction de l'arbre de rendu, mise en page et dessin Le temps dépendra de la taille du document, des styles appliqués et du périphérique sur lequel le document est exécuté : plus le document est volumineux, plus le navigateur doit travailler, plus le style est complexe, plus il est long ; il faudra pour dessiner (par exemple, dessiner une seule couleur). La surcharge est "plus petite", tandis que la surcharge de calcul et de rendu des ombres est "beaucoup plus grande").
Voici un bref aperçu des étapes effectuées par le navigateur :
Traitez le balisage HTML et construisez l'arborescence DOM.
Traitez le balisage CSS et créez l'arborescence CSSOM.
Fusionnez DOM et CSSOM dans un arbre de rendu.
Disposition selon l'arbre de rendu pour calculer les informations géométriques de chaque nœud.
Dessinez chaque nœud à l'écran.
Si le DOM ou le CSSOM est modifié, toutes les étapes ci-dessus doivent être effectuées à nouveau pour déterminer quels pixels doivent être restitués à l'écran.
L'optimisation du chemin de rendu critique consiste à minimiser le temps total passé à exécuter les étapes 1 à 5 de la séquence ci-dessus Cela permet de restituer le contenu à l'écran aussi rapidement. autant que possible et réduit également le temps entre les mises à jour de l'écran après le rendu initial ; c'est-à-dire, obtenez des taux de rafraîchissement plus élevés pour le contenu interactif.
Par défaut, le CSS est traité en tant que ressource bloquant le rendu (mais ne bloque pas l'analyse du HTML), ce qui signifie que le navigateur ne restituera aucun contenu traité tant que le CSSOM n'est pas construit. Assurez-vous de réduire votre CSS, de le servir le plus rapidement possible et d'exploiter les types de médias et les requêtes pour débloquer le rendu et réduire le temps passé au-dessus de la ligne de flottaison.
Dans la construction de l'arbre de rendu, DOM et CSSOM sont requis pour construire l'arbre de rendu. Cela peut avoir de sérieuses implications en termes de performances : HTML et CSS sont tous deux des ressources bloquant le rendu. Le HTML est évidemment obligatoire car sans le DOM, il n'y a rien à restituer, mais le besoin du CSS est peut-être moins évident. Que se passe-t-il si vous essayez d'afficher une page Web normale sans bloquer le rendu CSS ?
Par défaut, CSS est traité comme une ressource bloquant le rendu.
Nous pouvons marquer certaines ressources CSS comme non bloquantes via les types de médias et les requêtes multimédias.
Le navigateur téléchargera toutes les ressources CSS, qu'elles soient bloquantes ou non.
Les pages Web sans CSS sont pratiquement inutilisables. Le navigateur bloquera donc le rendu jusqu'à ce que le DOM et le CSSOM soient prêts.
CSS est une ressource bloquant le rendu. Il doit être téléchargé sur le client le plus tôt et le plus rapidement possible pour raccourcir le temps du premier rendu.
Que se passe-t-il si certains styles CSS ne sont utilisés que sous certaines conditions, par exemple lorsque la page Web est affichée ou projetée sur un grand écran ? Ce serait bien si ces ressources ne bloquaient pas le rendu.
De telles situations peuvent être résolues grâce aux « types de médias » et aux « requêtes multimédias » CSS :
媒体查询由媒体类型以及零个或多个检查特定媒体特征状况的表达式组成。例如,第一个样式表声明未提供任何媒体类型或查询,因此它适用于所有情况。也就是说它始终会阻塞渲染。第二个样式表则不然,它只在打印内容时适用---或许您想重新安排布局、更改字体等等,因此在网页首次加载时,该样式表不需要阻塞渲染。最后一个样式表声明提供了由浏览器执行的“媒体查询”:符合条件时,样式表会生效,浏览器将阻塞渲染,直至样式表下载并处理完毕。
通过使用媒体查询,我们可以根据特定用例(比如显示或打印),也可以根据动态情况(比如屏幕方向变化、尺寸调整事件等)定制外观。声明样式表时,请密切注意媒体类型和查询,因为它们将严重影响关键渲染路径的性能。
让我们考虑下面这些实例:
第一个声明阻塞渲染,适用于所有情况。
第二个声明同样阻塞渲染:“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);
无论我们使用