建構過程中可能會産生的阻塞
-
的代碼,是從上到下一行行執行的,也就是說如果html
代碼寫在js
頭裡,且沒有用加在head
方法裡,那麼他是無法讀取到window.onload
裡的标簽的。之是以加在body
裡可以執行,是因為,window.onload
裡的函數會在window.onload
樹加載之後執行。dom
- 在遇到
标簽後,會在link
加載(從伺服器下載下傳)完畢後,再執行後續代碼。但與此同時,如果還有外部檔案,則是同時加載(不阻塞後續外部檔案link
、link
加載)。但是外部檔案内的代碼不會執行,隻會在代碼解析到它的時候執行。script
-
标簽會阻塞script
解析,因為html
可能會改變js
和dom
,是以浏覽器會先解析script,避免浪費時間。 要想避免阻塞的話,可使用css
和defer
。async
- 對于動态建立的
标簽不會阻塞其後動态建立的link
的加載與執行,不管script
标簽是否具有script
屬性。async
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script>
var start = +new Date
</script>
</head>
<body>
test
<script>
var link = document.createElement('link')
link.href = 'http://udacity-crp.herokuapp.com/style.css?rtt=2'
link.rel = 'stylesheet'
document.head.appendChild(link)
var script = document.createElement('script')
script.src = 'http://udacity-crp.herokuapp.com/time.js?rtt=1&a'
document.head.appendChild(script)
</script>
<div id="result"></div>
<script>
var end = +new Date
document.getElementById('result').innerHTML = end - start
</script>
</body>
</html>
-
添加js
屬性之後,async
加載的外部檔案成為了異步加載,這時相當于它于原本的script
解析過程同步進行。是以他不會被任何加載過程阻塞,隻會在自己加載完成之後執行。但是,異步執行的影響就是,它如要讀取html
節點,很可能會失敗,因為它的加載和dom
解析過程沒有了先後順序。另外,如果它要輸出動态的html
節點,就無法保證節點的位置,因為它添加的節點,是在dom
已解析的節點下順序添加的。html
<script src="http://localhost:8080/test.js" async></script>
-
添加js
屬性之後,defer
加載的外部檔案成為了異步加載,執行是同步的。腳本加載不阻塞頁面的解析,腳本在擷取完後并不立即執行,而是等到script
樹加載完畢執行。DOM
- 如下代碼,加上
後會報錯defer
。Uncaught ReferenceError: $ is not defined
樹渲染結束前DOM
裡的body
已經執行了script

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script defer src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<div id="d1">我是内容</div>
<script >
console.log($('#d1').html())
</script>
</body>
</html>
- async 屬性表示異步執行引入的 JavaScript,與 defer 的差別在于,如果已經加載好,就會開始執行——無論此刻是 HTML 解析階段還是 DOMContentLoaded 觸發之後。
- defer 屬性表示延遲執行引入的 JavaScript,即這段 JavaScript 加載時 HTML 并未停止解析,這兩個過程是并行的。整個 document 解析完畢且 defer-script 也加載完成之後(這兩件事情的順序無關),會執行所有由 defer-script 加載的 JavaScript 代碼,然後觸發 DOMContentLoaded 事件
- defer 與相比普通 script,有兩點差別:載入 JavaScript 檔案時不阻塞 HTML 的解析,執行階段被放到 HTML 标簽解析完成之後。
什麼情況會引起重排/回流(reflow)
- 添加或者删除可見的DOM元素;
- 元素位置改變——display、float、position、overflow等等;
- 元素尺寸改變——邊距、填充、邊框、寬度和高度
- 内容改變——比如文本改變或者圖檔大小改變而引起的計算值寬度和高度改變;
- 頁面渲染初始化;
- 浏覽器視窗尺寸改變——resize事件發生時;
如何減少和避免重排
- Reflow 的成本比 Repaint 的成本高得多的多。一個節點的 Reflow 很有可能導緻子節點,甚至父節點以及兄弟節點的 Reflow 。
- 樣式集中改變
- 盡量不要在布局資訊改變時做查詢(會導緻渲染隊列強制重新整理)
- 同一個DOM的多個屬性改變可以寫在一起(減少DOM通路,同時把強制渲染隊列重新整理的風險降為0)
- 如果要批量添加DOM,可以先讓元素脫離文檔流,操作完後再帶入文檔流,這樣隻會觸發一次重排(fragment元素的應用)
- 将需要多次重排的元素,position屬性設為absolute或fixed,這樣此元素就脫離了文檔流,它的變化不會影響到其他元素。例如有動畫效果的元素就最好設定為絕對定位。
DOM是什麼?
DOM(Document Object Model——文檔對象模型)是用來呈現以及與任意 HTML 或 XML文檔互動的API。DOM 是載入到浏覽器中的文檔模型,以節點樹的形式來表現文檔,每個節點代表文檔的構成部分(例如:頁面元素、字元串或注釋等等)。
DOM的作用
- DOM 将HTML文檔呈現為帶有元素、屬性和文本的樹結構(節點樹)。
- 它允許運作在浏覽器中的代碼通路檔案中的節點并與之互動。節點可以被建立,移動或修改。事件監聽器可以被添加到節點上并在給定事件發生時觸發。