JS的並行加載與執行順序

 JavaScript文件(下面簡稱腳本文件)須要被html文件引用才能在瀏覽器中運行。在html文件中能夠經過不一樣的方式來引用腳本文件,咱們須要關注的是,這些方式的具體實現和這些方式可能會帶來的性能問題。javascript

當瀏覽器遇到(內嵌)<script>標籤時,當瀏覽器無從獲知JavaScript是否會修改頁面內容。所以,這是瀏覽器會中止處理頁面,限制性JavaScript代碼,而後再繼續解析和渲染頁面。一樣的狀況也會發生在使用src屬性加載JavaScript的過程當中(即外鏈JavaScript),瀏覽器必須先花時間下載外鏈文件中的代碼,而後解析並執行它。在這個過程當中,頁面渲染和用戶交互徹底被阻塞了。css

也就是說,每當瀏覽器解析到<script>標籤(不管是內嵌仍是外鏈)時,瀏覽器會(一根筋的)優先下載、解析並執行該標籤中的JavaScript代碼,而阻塞了其餘全部頁面內容的下載和渲染。html

五種引用腳本的方式:java

1.慣例的作法瀏覽器

最傳統的方式是在head標籤內插入<script>標籤,然而這種常規的作法卻隱藏着嚴重的性能問題,根據上述對<script>標籤特性的描述,咱們知道,在該例中,當瀏覽器解析到<script>標籤時,瀏覽器會中止解析其後的內容,而優先下載腳本文件,並執行其中的代碼,這意味着,其後的test.css樣式文件和<body>標籤都沒法被加載,因爲<body>標籤無被加載,那麼頁面天然就沒法渲染了。所以在該JavaScript代碼徹底執行以前,頁面都是一片空白。app

<script type="text/javascript"src="example.js"></script>函數

2.經典的作法性能

既然<script>標籤會阻塞其後內容的加載,那麼將<script>標籤放到全部頁面內容以後不就能夠避免這種糟糕的情況了嗎?將全部的<script>標籤儘量地放到<body>標籤底部,以儘可能避免對頁面其他部分下載的影響。然在IE8+瀏覽器上已經實現了腳本並行下載,可是在某些瀏覽器中(便是將腳本文件放在<body>標籤底部),頁面腳本還是一個接一個加載的,因此咱們須要下一個方法,即:動態加載腳本ui

3.動態腳本url

經過文檔對象模型(DRM),咱們幾乎能夠在頁面任何地方建立。

var script=document.createElement('script');

script.type='text/javascript';

script.src='file1.js';

document.getElementByTagName('head')[0].appendChild(script);

上述代碼動態地建立了一個外鏈file1的<script>標籤,並將其添加到<head>標籤內。這種技術的重點在於:

不管在什麼時候啓動下載,文件的下載和執行過程不會阻塞頁面其餘流程(包括腳本加載)。

 然而這種方法也是有缺陷的,這種方法加載的腳本會在下載完成後當即執行,那麼意味着多個腳本之間的運行順序是沒法保證的(除了Firefox和Opera)。當某個腳本對另外一個腳本有依賴關係時,就極可能發生錯誤了,好比,寫一個jQuary代碼,須要引入jQuary庫,然而你寫的jQuary極可能會先完成下載並當即執行,這是瀏覽器會報錯——jQuery庫還未下載完成,因而就作出如下改進:

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);

}

 上述代碼改進的地方就是增長了一個回調函數,該函數會在相應腳本加載完成後被調用。這樣即可以實現順序加載了,寫法以下(假設file2依賴file1,file1和file3相互獨立):

loadScript('file1.js',function(){loadScript('file2.js',function(){});})

loadScript('file3.js',function(){});

file2會在file2加載完成後纔開始加載。保證了在file2執行前file1已經準備穩當。而file1和file3是並行下載的,互不影響。雖然loadScript函數已經足夠好,但仍是有些不只以德地方——經過分析這段代碼,咱們知道,loadScript函數中的加載順序是以腳本的阻塞加載來實現的,而咱們真正想實現的是——腳本同步下載病案相應順序執行,即並行加載並順序執行。

4.LABjs

LABjs庫能幫咱們真正地實現「並行加載與順序執行」,推薦寫法以下:

<script src="LAB.js"></script>

     <script type="text/javaScript">

     $LAB

                .script("script1.js").wait()

                .script("script2-a.js")

                .script("script2-b.js")

                .wait(function(){

                       initScript1();

                       initScript2();

                              })

            <script>

5.requireJS

<script src="require.js"></script>

        require([

 "script1.js",

 "script2-a.js",

 "script2-b.js",

 "script3.js"

   ],

     function(){

               initScript1();

               initScript2();

               initScript3();

        }

 )

</script>

相關文章
相關標籤/搜索