本章我們討論javascript在瀏覽器中是如果工作的,包括:下載、解析、執行的全過程。javascript的這些討人嫌的地方我們是知道的:
i.需要串行下載
ii.需要解析
iii.需要串行執行
而在chrchromium中,js是這樣解析的:(其實第一章末尾已經有了)
至於一些步驟的解釋,這里就不再復述了,不懂的請戳:瀏覽器渲染過程 拉至末尾。
簡直就是大魔王有木有?心中可有一萬只草泥馬奔騰而過?為什么在所有的下載線程中:
i.css加載不會阻塞頁面
ii.images加載不會阻塞頁面
iii.flash加載不會阻塞頁面
iiii.activeX加載不會阻塞頁面
iiiii.ajax還有同步異步之分
特么javascript文件就會阻塞頁面!!
既然如此,我們能回避UI阻塞嗎
i.在頁面底部(</body>之前)引入js腳本,原因:由於js加載阻塞頁面,而HTML是下載多少渲染多少,因此我們把它至於頁面底部,讓UI線程先執行完再加載js腳本
ii.根據具體情況,通過combo和compress減少請求數(通常在正式生產環境,我們將多個js腳本壓縮為一個)
BUT ,這並沒有真正回避UI阻塞,在</body>之前存在一個較大的腳本需要加載執行時,UI在ready后,需要較長的時間等待腳本加載和執行,在腳本ready前,UI是處於無事件響應狀態的。
iii.defer屬性
HTML4標准中為<script>標簽定義的屬性,用於告訴瀏覽器:內容中包含document.write之類破壞DOM的腳本
瀏覽器會無阻塞式(延遲)加載腳本,並且按頁面中<script>標簽順序串行執行js腳本
在HTML渲染完畢之后,onload觸發之前執行
支持:IE4.0+ ,FF3.5+
iiii.async屬性
HTML5標准中為<script>標簽定義的屬性
對比defer,以下相同:
無阻塞式加載
以下不同:
加載完立即執行
不保證按照頁面中<script>標簽順序執行
支持:FF3.6+ ,Chrome ,Opera10.5+ ,Safari ,IE9+
iiiii.不依賴瀏覽器版本的方式
Dynamic Script DOM ,比如google分析:
XHR Inject
XHR Eval
Script in Iframe
還有問題嗎?當然還有:
i.並行、異步加載腳本也需要保證順序、同源策略、CDN、緩存等因素的影響
ii.沒有通用的解決方案,不過我們可以:
使用LABjs || requireJS || seaJS 等管理我們的腳本加載
服務端combo腳本
iii.以上可以相對完美的解決腳本下載問題,但這並不能解決腳本執行阻塞的問題
異步大法好
關於異步
i.你是否認為: 異步 == (方法 + 回調) ? 呵呵 ... 你懂得! 冒泡排序的異步例子:
var innerLoop = function (array, x, y, callback) { if (y < array.length - x) { compare(array[y], array[y + 1], function (r) { if (r > 0) { swap(array, y, y + 1, function () { innerLoop(array, x, y + 1, callback); }); } else { innerLoop(array, x, y + 1, callback); } }); } else { callback(); } } outerLoop(array, 0, function () { console.log("done!"); } ); var compare = function (x, y, callback) { setTimeout(10, function () { callback(x - y); }); } var swap = function (a, i, j, callback) { var t = a[i]; a[i] = a[j]; a[j] = t; repaint(a); setTimeout(20, callback); } var outerLoop = function (array, x, callback) { if (x < array) { innerLoop(array, x, 0, function () { outerLoop(array, x + 1, callback); }); } else { callback(); } }
ii.你是否認為: 瀏覽器是單線程執行的 ?
iii.你是否使用 setTimeout || setInterval 模擬過多線程 ?
iiii.異步編程是有難度的:
破壞了代碼的局部性
難以應用於需要保持順序的場景
難以處理異常及取消
難以操作異步之間的協作及組合
問題根源:
i.javascript是單線程語言
不能創建線程
不能開展並行任務
不能對線程操作
ii.阻塞UI渲染
如何解決:
i.瀏覽器並不是單線程的貨,我們可以利用如下異步線程:
資源下載線程(javacript、images、css、object)
ajax線程
web worker線程
setTimeout模擬多線程
總結
我們可以使用各種組合方式、現代瀏覽器的新特性去處理這些問題,總而言之,沒有一種方案是通用的。我們需要在對應的場景中找出最合適的方案,而這些方案無非是圍繞這些原理做出的變體。