並行載入與順序執行–《高效能javascript》讀書筆記(jquery載入順序)
並行載入與順序執行——《高效能javascript》讀書筆記
Javascript檔案(下面簡稱指令碼檔案)需要被HTML檔案引用才能在瀏覽器中執行(廢話嘛(-.-))。在HTML檔案中可以通過不同的方式來引用指令碼檔案,我們需要關注的是,這些方式的具體實現和這些方式可能會帶來的效能問題。
首先,引用指令碼必須用到<script>標籤,我們需要了解<script>標籤的特性,引述書中作者原話:
當瀏覽器遇到(內嵌)<script>標籤時,當前瀏覽器無從獲知Javascript是否會修改頁面內容。因此,這時瀏覽器會停止處理頁面,先執行Javascript程式碼,然後再繼續解析和渲染頁面。同樣的情況也發生在使用 src 屬性加在Javascript的過程中(即外鏈 Javascript ),瀏覽器必須先花時間下載外鏈檔案中的程式碼,然後解析並執行它。在這個過程中,頁面渲染和使用者互動完全被阻塞了。
通過上述描述,我們能瞭解到:
每當瀏覽器解析到<script>標籤(無論內嵌還是外鏈)時,瀏覽器會(一根筋地)優先下載、解析並執行該標籤中的Javascript程式碼,而阻塞了其後所有頁面內容的下載和渲染。
下面列舉四種引用指令碼的方式:
慣例的做法
最傳統的方式是在head標籤內插入<script>標籤:
1 2 3 4 5 6 7 8 9 10 |
<html> <head> <title>Example</title> <script type <link rel="stylesheet" type="text/css" href="styles/test.css" /> </head> <body> <p>Vtmer Rush~~</p> </body> </html> |
然而這種常規的做法卻隱藏著嚴重的效能問題。根據上述對<script>標籤特性的描述,我們知道,在該示例中,當瀏覽器解析到<script>標籤(第4行)時,瀏覽器會停止解析其後的內容,而優先下載指令碼檔案,並執行其中的程式碼,這意味著,其後的test.css樣式檔案和<body>標籤都無法被載入,由於<body>標籤無法被載入,那麼頁面自然就無法渲染了。因此在該javascript程式碼完全執行完之前,頁面都是一片空白。
下面貼上一張動態圖來說明上述現象:
上圖有兩點是需要注意的:
- 頁面的渲染和javascript程式碼的執行是一起顯示出來的。這說明頁面最開始出現的空白正是由於javascript檔案阻塞特性引起的(為了突出這一現象,我特意外鏈了幾個體積較大的js庫,然後迅雷火力全開…(-.-))。這是因為如果javascript檔案沒有阻塞頁面渲染的話,頁面的渲染一般會先於javascript檔案的載入(一般來說頁面所需要的的css樣式檔案和html檔案的體積會遠遠小於javascript檔案,如果沒有被阻塞,它們會先於javascript檔案下載好,然後立即被瀏覽器解析出來),請對比無阻塞的效果圖點這裡。
- 圖片的載入是在javascript執行之後才開始的,即javascript阻塞了圖片的載入。
經典的做法
既然<script>標籤會阻塞其後內容的載入,那麼將<script>標籤放到所有頁面內容之後不就可以避免這種糟糕的狀況了嗎?如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<html> <head> <title>Example</title> <link rel="stylesheet" type="text/css" href="styles/test.css" /> </head> <body> <p>Vtmer Rush~~</p> <!-- 推薦的指令碼放置位置 --> <script type='text/javascript' src='file1.js'></script> <script type='text/javascript' src='file2.js'></script> <script type='text/javascript' src='file3.js'></script> </body> </html> |
將所有的<script>標籤儘可能地放到<body>標籤底部,以儘量避免對頁面其餘部分下載的影響,效果圖如下:
對比上一張動態圖,頁面渲染先於指令碼檔案的執行,說明指令碼檔案不再阻塞頁面渲染了(包括css檔案和img等檔案的下載)
然而作者在後面又介紹了另一種方式——動態載入指令碼。起初我不太明白,把指令碼放到<body>底部就好了,為什麼還需要動態指令碼?多翻了幾回書才發現原來自己忽略了作者的一段話:
(將指令碼放到<body>標籤底部時)儘管指令碼下載會阻塞另一個指令碼,但是頁面的大部分內容已經下載完成並顯示給使用者…
即是說,雖然在IE8+瀏覽器上已經實現了指令碼並行下載,但在某些瀏覽器中(即使指令碼檔案放到了<body>標籤底部),頁面中指令碼仍是一個接著一個載入的。就上述示例程式碼來說,瀏覽器先載入完file1,再去載入file2,最後才輪到file3。雖然此時指令碼已經不影響其他頁面內容了,但我們也同樣希望指令碼之間實現並行下載(即同時開始下載),於是下面給出動態載入指令碼的方法來實現這一想法。
動態指令碼
通過文件物件模型(DOM),我們可以幾乎可以頁面任意地方建立<script>標籤:
1 2 3 4 |
var script=document.createElement('script'); script.type='text/javascript'; script.src='file1.js'; document.getElementsByTagName('head')[0].appendChild(script); |
上述程式碼動態建立了一個外鏈file1的<script>標籤,並將其新增到<head>標籤內。這種技術的重點在於:
無論在何時啟動下載,檔案的下載和執行過程不會阻塞頁面其他程序(包括指令碼載入)。
然而這種方法也是有缺陷的。這種方法載入的指令碼會在下載完成後立即執行,那麼意味著多個指令碼之間的執行順序是無法保證的(除了Firefox和Opera)。當某個指令碼對另一個指令碼有依賴關係時,就很可能發生錯誤了。比如,寫一個jQuery程式碼,需要引入jQuery庫,然而你寫的jQuery程式碼檔案很可能會先完成下載並立即執行,這時瀏覽器會報錯——‘jQuery未定義’之類的,因為此時jQuery庫還未下載完成。於是做出以下改進:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function loadScript(url,callback){ var script=document.createElement('script'); script.type="text/javascript"; if(script.readyState){//IE script.onreadystatechange=function(){ if(script.readyState=="loaded"||script.readyState=="complete"){ script.onreadystatechange=null; callback(); } }; }else{//其他瀏覽器 script.onload=function(){ callback(); }; } script.src=url; document.getElementsByTagName('head')[0].appendChild(script); } |
上述程式碼改進的地方就是增加了一個回撥函式(4-11行),該函式會在相應指令碼檔案載入完成後被呼叫。這樣便可以實現順序載入了,寫法如下(假設file2依賴file1,file1和file3相互獨立):
1 2 3 4 |
loadScript('file1.js',function(){ loadScript('file2.js',function(){}); }); loadScript('file3.js',function(){}); |
file2會在file1載入完後才開始載入,保證了在file2執行前file1已經準備妥當。而file1和file3是並行下載的,互不影響。
雖然loadScript函式已經足夠好,但還是有些不盡人意的地方——通過分析這段程式碼,我們知道,loadScript函式中的順序載入是以指令碼的阻塞載入來實現的(正如上述紅字部分指出的那樣)。而我們真正想實現的是——指令碼同步下載並按相應順序執行,即並行載入並順序執行。
LABjs庫
LABjs庫能幫我們真正地實現“並行載入與順序呢執行”,推薦寫法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<html> <head> <title>Example</title> <link rel="stylesheet" type="text/css" href="styles/test.css" /> </head> <body> <p>Vtmer Rush~~</p> <!-- 推薦的指令碼放置位置 --> <script type='text/javascript' src='LABjs.js'></script> <script type='text/javascript'> $LAB.script('file1.js') .script('file2.js') .script('file3.js'); </script> </body> </html> |
上述程式碼會並行載入三個指令碼檔案,更讚的是,它會按指令碼的順序來執行這些指令碼。瀑布圖示例如下:
可從圖中看出,jQuery程式碼檔案(text.js)與jQuery庫檔案並行載入,而且先於庫檔案完成下載,此時頁面能正常執行,說明頁面是按順序執行指令碼檔案的。
注:
- 以上是一些讀書筆記及心得,整理至《高效能javascript》,希望能對大家有所幫助
- 適遠師兄和hbc提到的seajs和require.js貌似也可以實現,有空去試試
相關推薦
並行載入與順序執行–《高效能javascript》讀書筆記(jquery載入順序)
並行載入與順序執行——《高效能javascript》讀書筆記 Javascript檔案(下面簡稱指令碼檔案)需要被HTML檔案引用才能在瀏覽器中執行(廢話嘛(-.-))。在HTML檔案中可以通過不同的方式來引用指令碼檔案,我們需要關注的是,這些方式的具體實現和這些
高效能 JavaScript 讀書筆記(四)
迴圈 1.減少迭代的工作量 2.倒序迴圈 條件語句 if-else && switch 大多數情況下,switch 比 if-else 執行的快。但只有條件數量很大的時候才明顯。 優化 if-else 1.把最可能出現的條件放在最前面。 2.巢
高效能 JavaScript 讀書筆記(五)
字串和正則表示式 字串連線 連線字串有很多方式,常見的有: 1.str = "a" + "b" + "c"; 2.str = "a"; str += "b"; str += "c"; 3.str = ["a","b","c"].join(""); 4.str = "
高效能 JavaScript 讀書筆記(三)
重繪與重排 概念 重排:當元素的寬和高發生改變的時候,就會重新排列元素,稱為重拍。 重繪:將重排的的元素重新繪製在瀏覽器中,稱為重繪。 在元素中新增段落、新增邊框、新增或者刪除元素、修改內外邊距等,一切改變現有元素的位置的操作都會導致重排的發生。相應的,新增背景色這一類沒有改變元素位
高效能 JavaScript 讀書筆記 (二)
資料存取 字面量、本地變數、陣列元素、物件成員 字面量只代表自身,不儲存在特定的位置。JavaScript 中的字面量有:字串、數字、布林值、物件、陣列、函式、正則表示式以及特殊的 null 和 undefined 值。 一般來說,訪問本地變數和字面量的事件較少,訪問陣列元素和物件成員花
資料結構與演算法JavaScript描述讀書筆記(js實現樹)
js定義二叉查詢樹 //建立建構函式建立節點 function Node(data){ this.data = data; this.left = null; this.right = null; } function tree(){ this.root = nu
資料結構與演算法JavaScript描述讀書筆記(高階排序演算法)
希爾排序 在插入排序的基礎上,只不過比較的步長不一樣,插入排序比較步長一直是1(即一個一個的比較)。希爾排序的步長第一次一般設定為gap=Math.floor(arr.length/2),之後依次將步長設定為gap/2,直到步長變為1,這個時候徹底轉化成插入排 測試時間普通排序演算法1
資料結構與演算法JavaScript描述讀書筆記(基本排序演算法)
前提準備 //自動生成陣列的函式,n:整數個數,數字在l-r之間 function setData(n,l,r){ var dataStore = []; for(var i=0;i<n;i++){ dataStore[i] = Math.floor(
高效能javascript讀書筆記之操作DOM
DOM:文件物件模型,是一個獨立於語言的,用來操作XML和HTML文件的程式介面(API)。 瀏覽器通常會把DOM和javascript獨立實現,每次連線DOM和ECMAScript,都會被收取“過橋費”(效能消耗)。 優化方法: 1.減少訪問DOM的次數,把運算儘量留在
高效能javascript讀書筆記之注意點
1.避免雙重求值 允許傳人另一段javascript程式碼字串並執行的四種方法: eval()、Function()建構函式、setTimeout()和setInterval() 當在javascript程式碼中執行另一段javascript程
編寫高效能JavaScript (讀書筆記)
轉載自http://kb.cnblogs.com/page/501177/ 作者: Addy Osmani 讀書筆記: 作者以V8為例講解: 一.核心構成部分 二.垃圾回收GC:Garbage Collecation 更喜歡叫做資源回收。 Q1:在JavaScript
android BSP與硬件相關子系統讀書筆記(1)android BSP移植綜述
tin 不用 googl csdn splay 編寫 app開發 意圖過濾器 trac 從linux驅動轉行至Android驅動開發大半年了,一開始就產生了一個很糾結目標和問題,就是不停的google如何porting android!這個問題得到的結果對於初出茅廬的我,感
高效能JavaScript學習筆記(5)--程式設計實踐
1、eval()效率很低,因為每次呼叫都要建立一個新的直譯器/編譯器例項。2、setTimeout()和setInterval()建議傳入函式而不是字串來作為第一引數。 例如: setTime
高效能mysql讀書筆記(一)
1 mysql的架構與歷史: 1.1 mysql邏輯架構 mysql將查詢等處理與資料儲存相分離的,可以更加靈活的選取資料儲存的方式。mysql處理架構:連線處理->解析器->查詢快取->優化器->儲存引擎。 第一層:連線處理:包括主要的授權登入、安
高效能MySQL.讀書筆記(六)高可用性
什麼是高可用性 每個應用對可用性的需求各不相同。在設定一個可用時間的目標之前,先問問自己,是不是確實需要達到這個目標。可用性每提高一點,所花費的成本都會遠超之前;可用性的效果和開銷的比例並不是線性的。需要保證多少可用時間,取決於能夠承擔多少成本。高可用性實際上是在宕機造成的
高效能MySQL.讀書筆記(一)優化伺服器設定
MySQL有大量可以修改的引數——但不應該隨便去修改。通常只需要把基本的配置項配置正確(大部分情況下只有很少一些引數是真正重要的),應該將更多的時間花在schema的優化、索引,以及查詢設計上。 確保基本的配置是正確的,如果碰到了問題,並且問題是由於伺服器的某部分導致的,而這恰好可以通過某個配置項解決,那麼需
高效能mysql讀書筆記(四)
四,建立高效能的索引 索引也稱為鍵,索引是一種查詢資料有用的資料結構,索引優化是查詢優化中最優效的手段。 索引可以包含一列或者多列,mysql能夠高效的使用索引中的最左字首列,索引在儲存引擎層實現,不同的儲存引擎有不同的索引。B-tree索引作為大多數資料庫的索引資料結構,
高效能MySQL.讀書筆記(二)作業系統和硬體優化
使用Flashcache 雖然有很多因素需要在快閃記憶體、硬碟和RAM之間權衡,在儲存層次結構中,這些裝置沒有被當作一個整體處理。有時可以使用磁碟和記憶體技術的結合,這就是Flashcache。 Flashcache是一個Linux核心模組,使用Linux的裝置對映器(Device Mapper)。它在記憶體
《大型網站系統與Java中介軟體》讀書筆記(上)
前言 只有光頭才能變強。 文字已收錄至我的GitHub倉庫,歡迎Star:https://github.com/ZhongF
《大型網站系統與Java中介軟體》讀書筆記 (中)
前言 只有光頭才能變強。 文字已收錄至我的GitHub倉庫,歡迎Star:https://github.com/ZhongF