Heim > Artikel > Web-Frontend > Der Einfluss von JS, CSS und img auf die Fähigkeiten von DOMContentLoaded event_javascript
前端的纯技术就是对规范的认知
什么是DOMContentLoaded事件?
首先想到的是查看W3C的HTML5规范,DOMContentLoaded事件在什么时候触发:
Sobald der Benutzeragent das Parsen des Dokuments beendet, muss der Benutzeragent die folgenden Schritte ausführen:
1. Setzen Sie die aktuelle Dokumentbereitschaft auf „interaktiv“ und den Einfügepunkt auf undefiniert.
Entfernen Sie alle Knoten vom Stapel offener Elemente.
2. Wenn die Liste der Skripte, die nach Abschluss der Analyse des Dokuments ausgeführt werden, nicht leer ist, führen Sie die folgenden Unterschritte aus:
2.1 Drehen Sie die Ereignisschleife, bis für das erste Skript in der Liste der Skripte, die ausgeführt werden, wenn die Analyse des Dokuments abgeschlossen ist, das Flag „Bereit zur Parser-Ausführung“ gesetzt ist und das Dokument des Parsers kein Stylesheet hat, das Skripte blockiert.
2.2 Führen Sie das erste Skript in der Liste der Skripte aus, die ausgeführt werden, wenn die Analyse des Dokuments abgeschlossen ist.
2.3 Entfernen Sie das erste Skriptelement aus der Liste der Skripte, die ausgeführt werden, wenn das Dokument mit der Analyse fertig ist (d. h. den ersten Eintrag in der Liste verschieben).
2.4 Wenn die Liste der Skripte, die nach Abschluss der Analyse des Dokuments ausgeführt werden, immer noch nicht leer ist, wiederholen Sie diese Unterschritte erneut ab Unterschritt 1.
3. Stellen Sie eine Aufgabe in die Warteschlange, um ein einfaches Ereignis mit dem Namen DOMContentLoaded im Dokument auszulösen.
规范总是那么的晦涩,但至少有一点是可以明确了的,就是在JS(不包括动态插入的JS)执行完之后,才会触发DOMContentLoaded事件.
接下来看看MDN上有关DOMContentLoaded事件的文档:
<.>Das DOMContentLoaded-Ereignis wird ausgelöst, wenn das Dokument vollständig geladen und analysiert wurde, ohne darauf zu warten, dass Stylesheets, Bilder und Subframes vollständig geladen werden
Hinweis: Stylesheet lädt die Blockskriptausführung. Wenn Sie also ein<font face="NSimsun"><script></script></font>
nach einem<font face="NSimsun"><link rel="stylesheet" ...></font>
haben, wird die Analyse der Seite nicht abgeschlossen – und DOMContentLoaded wird nicht ausgelöst –, bis das Stylesheet geladen ist.
<font face="NSimsun">interactive</font>
而JS的执行,需要等待位于它前面的CSS加载(如果是外联的话)、执行完成,因为JS可能会依赖位于它前面的CSS计算出来的样式.实验1:DOMContentLoaded事件不直接等待CSS文件、图片的加载完成
index.html:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" type="text/css" href="./css/main.css" rel="external nofollow" rel="external nofollow" > </head> <body> <p>Content</p> <img src="./img/chrome-girl.jpg" alt="Der Einfluss von JS, CSS und img auf die Fähigkeiten von DOMContentLoaded event_javascript" > </body> </html>
如果页面中没有script标签, DOMContentLoaded事件并没有等待CSS文件、图片加载完成.
Chrome开发者工具的Timeline面板可以帮我们记录下浏览器的一举一动.图一中红色小方框中的蓝线, Laden Sie DOMContentLoaded herunter und laden Sie es zuerst malen, 鼠标 schweben 在这些线露出灰色方框下面的一小部分时就会出现带有说明文字的tips(这交互够反人类的对吧!)。
实验2:DOMContentLoaded事件需要等待JS执行完才触发
index.html:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> console.timeStamp('Inline script before link in head'); window.addEventListener('DOMContentLoaded', function(){ console.timeStamp('DOMContentLoaded event'); }); </script> <link rel="stylesheet" type="text/css" href="./css/main.css" rel="external nofollow" rel="external nofollow" > <script type="text/javascript"> console.timeStamp('Inline script after link in head'); </script> </head> <body> <p>Content</p> <img src="./img/chrome-girl.jpg" alt="Der Einfluss von JS, CSS und img auf die Fähigkeiten von DOMContentLoaded event_javascript" > <script type="text/javascript" src="./js/main.js"></script> </body> </html>
main.js:
console.timeStamp('External script after link in body');
图二
如果页面中静态的写有script标签,DOMContentLoaded事件需要等待JS执行完才触发。
而script标签中的JS需要等待位于其前面的CSS的加载完成。
console.timeStamp() 可以向Timeline中添加一条记录,并对应上方的一条黄线。
从图二中可以看出,在CSS之前的JS立刻得到了执行,而在CSS之后的JS,需要等待CSS加载完后才执行,比较明显的是main.js早就加载完了,但还是要等main.css加载完才能执行。而DOMContentLoaded事件,则是在JS执行完后才触发。滑动Timeline面板中表示展示区域的滑块,如图三,放大后即可看到表示DOMContentLoaded事件的蓝线(之前跟黄线和绿线靠的太近了),当然,通过 console.timeStamp() 向TimeLine中添加的记录也可证明其触发时间。
图三
现代浏览器会并发的预加载CSS, JS,也就是一开始就并发的请求这些资源,但是,执行CSS和JS的顺序还是按原来的依赖顺序(JS的执行要等待位于其前面的CSS和JS加载、执行完)。先加载完成的资源,如果其依赖还没加载、执行完,就只能等着。
实验3:img何时开始解码、绘制?
从图三中我们可以发现一个有趣的地方:img的请求老早就发出了,但延迟了一段时间才开始解码。如图二、图三中的红框所示,截图中只框出了一部分表示解码的记录,而实际上这些表示解码的记录一直持续到img加载结束,如图四所示,img是一边加载一边解码的:
图三
现代浏览器会并发的预加载CSS, JS,也就是一开始就并发的请求这些资源,但是,执行CSS和JS的顺序还是按原来的依赖顺序(JS的执行要等待位于其前面的CSS和JS加载、执行完)。先加载完成的资源,如果其依赖还没加载、执行完,就只能等着。
实验3:img何时开始解码、绘制?
从图三中我们可以发现一个有趣的地方:img的请求老早就发出了,但延迟了一段时间才开始解码。如图二、图三中的红框所示,截图中只框出了一部分表示解码的记录,而实际上这些表示解码的记录一直持续到img加载结束,如图四所示,img是一边加载一边解码的:
抱着“猜想——验证”的想法,我猜想这是因为img这个资源是否需要展现出来,需要等 所有的JS和CSS的执行完 才知道,因为main.js可能会执行某些DOM操作,比如删除这个img元素,或者修改其src属性,而CSS可能会将其 <font face="NSimsun">display: none</font>
。
图五中没有JS和CSS,img的数据一接收到就马上开始解码了。
图六中没有JS,但img要等到CSS加载完才开始解码。
图七的代码跟图六的代码唯一的区别是CSS把img给 <font face="NSimsun">display: none;</font>
,这使得img虽然请求了,但根本没有进行解码。
这说明,img是否需要解码、绘图(paint)出来,确实需要等CSS加载、执行完才能知道。也就是说,CSS会阻塞img的展现!那么JS呢?
图八对应的代码:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> console.timeStamp('Inline script in head'); window.addEventListener('DOMContentLoaded', function(){ console.timeStamp('DOMContentLoaded event'); }); </script> </head> <body> <p>Content</p> <img src="./img/chrome-girl.jpg" alt="Der Einfluss von JS, CSS und img auf die Fähigkeiten von DOMContentLoaded event_javascript" > <script type="text/javascript" src="./js/main.js"></script> </body> </html>
Es ist sehr überraschend, dass img auf einer Seite mit JS, aber ohne CSS, tatsächlich sofort nach dem Empfang der Daten mit dem Dekodieren und Zeichnen (Malen) beginnen kann. Mit anderen Worten, JS blockiert nicht die Anzeige von img! Dies unterscheidet sich von unserem bisherigen Verständnis des traditionellen Konzepts, dass JS IMG-Ressourcen blockiert. Es scheint, dass Chrome neue Optimierungen für das Laden und Anzeigen von IMG vorgenommen hat.
Die Methode $(document).ready(), die wir üblicherweise in jQuery verwenden, dient der Überwachung des DOMContentLoaded-Ereignisses (natürlich bietet sie auch eine interne Downgrade-Lösung durch Simulation des DOMContentLoaded-Ereignisses und Überwachung des Onload-Ereignisses). Generell wird empfohlen, Ereignisse für DOM-Elemente zu registrieren, wenn das DOMContentLoaded-Ereignis ausgelöst wird. Das Auslösen des DOMContentLoaded-Ereignisses so schnell wie möglich bedeutet also, dass die Seite so schnell wie möglich interaktiv gemacht werden kann:
Reduzieren Sie die Größe von CSS-Dateien, teilen Sie eine einzelne CSS-Datei zum parallelen Laden in mehrere Dateien auf und reduzieren Sie die Blockierungszeit von CSS auf JS
Sekundäre JS-Dateien werden durch dynamisches Einfügen von Skript-Tags geladen (dynamisch eingefügte Skript-Tags blockieren nicht das Auslösen des DOMContentLoaded-Ereignisses)
Sprites, die in CSS verwendet werden, können zusammen mit der CSS-Datei in HTML geladen werden, indem Sie img
vorab ladenWährend des Experiments hatte ich das Gefühl, dass das Timeline-Panel der Chrome-Entwicklertools sehr leistungsfähig ist und jede Bewegung des Browsers aufgezeichnet wurde. Wenn wir in der Vergangenheit das interne Verhalten des Browsers in der Front-End-Entwicklung verstehen und erforschen wollten, führten wir entweder Black-Box-Tests durch, indem wir die Steine befühlten, oder untersuchten den Browser-Quellcode mit doppeltem Ergebnis und halbem Aufwand. Der einzig effiziente Weg bestand darin, aus den Forschungserfahrungen anderer zu lernen und sich die Forschungserfahrungen von Ausländern anzusehen, aber die Entwicklung von Browsern ändert sich von Tag zu Tag (zum Beispiel blockiert das in diesem Experiment gefundene JS nicht die Anzeige von img ), die Erfahrung anderer Menschen ist nie die neueste und am besten geeignete. Der Schlüssel liegt darin, Ihre eigenen Geschäfts- und Nachfrageszenarien zu kombinieren und gezielte Entscheidungen zu treffen Analyse und Optimierung.
PS.
Die obige Testumgebung ist Windows/Chrome und Fiddler wird verwendet, um ein langsames Netzwerk zu simulieren