當初始的 HTML 文檔被徹底加載和解析完成以後,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。另外一個不一樣的事件 load 應該僅用於檢測一個徹底加載的頁面。 在使用 DOMContentLoaded 更加合適的狀況下使用 load 是一個使人難以置信的流行的錯誤,因此要謹慎。注意:DOMContentLoaded 事件必須等待script以前的樣式表加載解析完成纔會觸發。javascript
// 客戶端 html 代碼
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
document.addEventListener('DOMContentLoaded',function(){
console.log('3 seconds passed');
});
</script>
<link rel="stylesheet" href="http://localhost:3000/sleep">
</head>
<body>
<div><a >1234567</a></div>
</body>
// express 代碼
var express = require('express');
var app = express();
app.get('/sleep', function(req, res, next) {
res.type('text/css')
setTimeout(()=>{
res.send("a {\n" +
" color: red;\n" +
"}")
},3000)
});
複製代碼
控制檯當即顯示 3 seconds passed。 頁面是空白的。 過三秒後,頁面直接顯示紅字。css
頁面是空白的。過三秒後,html
控制檯顯示 3 seconds passed。java
頁面直接顯示紅字。express
由於 DOM 的渲染須要 DOM 樹和 CSSOM 樹共同來生成渲染樹,因此在 CSS 加載完成以前, DOM 是不會進行渲染的。仍是用上面那個例子,咱們會發現 CSS 沒有加載完成時,頁面上是不顯示的,而當 CSS 加載完成的瞬間,標籤就被渲染到頁面上了。瀏覽器
結果:頁面先顯示出div內容,過三秒後,字體顏色改變+控制檯打印。緩存
// html內的代碼
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="http://localhost:3000/sleep">
<script defer src="js/test.js"></script>
</head>
<body>
<div><a >1234567</a></div>
</body>
// js/test.js
const div = document.querySelector('div');
console.log(div);
複製代碼
此時咱們的 css 還沒加載完成,可是咱們能夠看到 console 選項卡里面 div 標籤已經被打印出來了,說明,此時 DOM 已經解析完成觸發 DOMContentLoaded 事件。bash
<script>
var starttime = new Date().getTime();
console.log("page start" + starttime);
</script>
<link rel="stylesheet" href="http://localhost:3000/sleep">
<script src="js/test.js"></script>
// js/test.js
var endtime = new Date().getTime();
console.log("delay:" + (endtime - starttime));
複製代碼
sleep 3秒後才返回 網絡
當解析起遇到 script 標籤時,文檔會當即中止解析直到腳本執行完畢,若是腳本是外部的,那麼解析過程會中止,直到從網絡同步抓取資源完成後再繼續。由於咱們的腳本會操做 DOM,因此在腳本跑完以前瀏覽器不知道腳本會把 DOM 改爲什麼樣,因此就等腳本執行完再進行解析。app
<script >
let ui = document.getElementById('ui')
ui.innerText = 'blue'
</script>
<div id="ui">test3</div>
複製代碼
從代碼和圖中看到,當把dom放在js下面,取不到元素,報錯。
GUI 渲染線程與 JS 引擎線程是互斥的,當 JS 引擎執行時 GUI 線程會被掛起(至關於被凍結了),GUI 更新會被保存在一個隊列中等到 JS 引擎空閒時當即被執行。
<div id="ui">test3</div>
<script >
var now = new Date().getTime();
let ui = document.getElementById('ui')
while (new Date().getTime() - now < 3000) {
continue;
}
ui.innerText = 'blue'
</script>
複製代碼
<div id="ui">test3</div>
<script src="js/test.js"></script>
// js/test.js 內容與上面script內的內容同樣
複製代碼
另外一套測試代碼
// 引入遠程文件
<script src="http://localhost:3000/test.js"></script>
// express 服務端代碼,2秒後再返回
app.get('/test.js', function(req, res, next) {
res.type('application/javascript')
setTimeout(()=>{
res.send(`alert()`)
},2000)
});
複製代碼
瀏覽器執行 css dom js 在同步模式下,都是從上往下執行。 渲染等到 JS 引擎空閒時當即被執行。
主要緣由仍是script內代碼有沒有加載完,若是沒有加載完就會先渲染一次,加載完就會先執行js,看js有沒有改變dom,避免浪費渲染一次,很智能。
比起js的懶加載, css懶加載和初始加載最小體積會更加提升性能。
async 模式下,JS 不會阻塞瀏覽器作任何其它的事情。它的加載是異步的,當它加載結束,JS 腳本會當即執行。
defer 模式下,JS 的加載是異步的,執行是被推遲的。等整個文檔解析完成、DOMContentLoaded 事件即將被觸發時,被標記了 defer 的 JS 文件纔會開始依次執行。
從應用的角度來講,通常當咱們的腳本與 DOM 元素和其它腳本之間的依賴關係不強時,咱們會選用 async;當腳本依賴於 DOM 元素和其它腳本的執行結果時,咱們會選用 defer。
一句話,defer是「解析完dom再執行」,async是「下載完就執行」。另外,若是有多個defer腳本,會按照它們在頁面出現的順序加載,而多個async腳本是不能保證加載順序的。
<link>
元素的 rel 屬性的屬性值preload可以讓你在你的HTML頁面中 <head>
元素內部書寫一些聲明式的資源獲取請求,能夠指明哪些資源是在頁面加載完成後即刻須要的。對於這種即刻須要的資源,你可能但願在頁面加載的生命週期的早期階段就開始獲取,在瀏覽器的主渲染機制介入前就進行預加載。這一機制使得資源能夠更早的獲得加載並可用,且更不易阻塞頁面的初步渲染,進而提高性能。
// 預加載Css
<link rel="preload" as="style" href="async_style.css" onload="this.rel='stylesheet'">
// 預加載js
<link rel="preload" as="script" href="async_script.js"
onload="var script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">
複製代碼
瀏覽器會查找關係類型(rel)爲 next 或 prefetch 的 HTML 或 HTTP Link: header。例子:
<link rel="prefetch" href="/images/big.jpeg">
<link rel="prefetch alternate stylesheet" title="Designed for Mozilla" href="mozspecific.css">
<link rel="next" href="2.html">
複製代碼
已經被許多瀏覽器支持了至關長的時間,但它是意圖預獲取一些資源, 以備下一個導航/頁面使用(好比,當你去到下一個頁面時)。 這很好,但對當前的頁面並無什麼助益。 此外,瀏覽器會給使用prefetch的資源一個相對較低的優先級——與使用preload的資源相比。畢竟,當前的頁面比下一個頁面相對更加劇要。
<link rel="stylesheet" href="http://localhost:3000/sleep2.load" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload1" >
<link rel="prefetch" as="style" href="http://localhost:3000/sleep2.prefetch" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload2" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload3" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload4" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload5" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload6" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload7" >
<link rel="stylesheet" href="http://localhost:3000/sleep.load" >
複製代碼
從下圖看出,preload比正常load優先級大,會優先佔用tcp連接,即便sleep2.load
寫在最上面,也要等6條tcp佔用釋放一條。prefetch 會在空閒時再使用。