Javascript的裝載和執行

風靈使發表於2018-05-30

通常來說,瀏覽器對於Javascript的執行有兩大特性:

  1. 載入後馬上執行
  2. 執行時會阻塞頁面後續的內容(包括頁面的渲染/其他資源的載入)。於是,如果有多個js檔案被引入,那麼對於瀏覽器來說,這些js檔案被穿行地載入,並一次執行。

因為javascrit可能會操作HTML文件的DOM樹,所以,瀏覽器一般都不會像並行下載css檔案並行下載js檔案,因為這是js檔案的特殊性造成的。所以,如果你的javascript像操作後面的DOM元素,基本上來說,瀏覽器都會報錯說物件找不到。因為javascript執行時,後面的html被阻塞住了,DOM樹時還沒有後面的DOM節點。所以程式也就報錯了。

傳統的方式

所以,當你在程式碼中寫下如下程式碼:

    <script type="text/javascript" src="http://coolshell.cn/asyncjs/alert.js"></script>

基本上來說,head裡的script標籤會阻塞後續資源的載入以及整個頁面的生成。

所以,你知道為啥那麼有很多網站把javascript放在網頁的最後面了,要麼就是動用window.onload或是$(document).ready(function(){})之類的事件。

另外,因為絕大多數哦的javascrit程式碼並不需要等頁面,即:非同步載入。那我們如何非同步載入呢?

document.write 方式

於是,你可能以為document.write()這種方式能夠解決阻塞問題。你當然會覺得,document.write了的script標籤後就可以執行後面的東西了,者沒錯。對於在同一個script標籤例的javascript的程式碼來說,是這樣的,但是對於整個頁面來說,這個還是會阻塞,下面時一段測試程式碼:


    <script type="text/javascript" language="javascript">
        function loadjs(script_filename) {
            document.write('<' + 'script language="javascript" type="text/javascript"');
            document.write(' src="' + script_filename + '">');
            document.write('<'+'/script'+'>');
            alert("loadjs() exit...");
        }

        var script = 'http://coolshell.cn/asyncjs/alert.js';

        loadjs(script);
        alert("loadjs() finished!");
    </script>

    <script type="text/javascript" language="javascript">
       alert("another block");
    </script>

依此彈出的對話方塊為:

loadjs() exit...
loadjs() finished!
hello world
another block

然後才會顯示頁面。

scriptdeferasync屬性

IE自從IE6就支援defer,如:

    <script defer type="text/javascript" src="./alert.js" ></script>

對於IE來說,這個標籤會讓IE並行下載js檔案,並且把其執行hold到了整個DOM裝載完畢(DOMContentLoaded),多個deferscript在窒息感時也會按照其出現的順序來執行。但是因為這個defer只時IE專用,所以一般用的比較少。

而我們標磚的HTML5也加入了一個非同步載入javascript的屬性:async,無論你對它都什麼樣的值,只要它出現,它就開始非同步載入js檔案。但是,async的非同步載入會有一個嚴重的問題,那就是它忠實地鹼性者“載入後馬上執行”這條軍規,所以,雖然它並不阻塞頁面渲染,但是你也無法控制他執行的次序和時機。

支援async標籤的瀏覽器是:Firefox3.6+,Chrome8.0+,Safari5.0,IE10+。Opera還不支援參考這裡所以這個方法也不是太好。因此不是所有的瀏覽器你都能行。
這裡寫圖片描述

動態建立DOM方式

這種方式可能是用的最多的了。


    function loadjs(script_name){
        var script = doucment.createElement('script');
        script.setAttribute('type', 'text/javascript');
        script.setAttribute('src', script_name);
        script.setAttribute('id', 'my_script_id');

        script_id = document.getElementById('my_script_id');
        if(script_id){
            document.getElementsByTagName('head')[0].removeChild(script_id);
        }
        document.getElementsByTagName('head')[0].appendChild(script);   
    };

    var script = 'http://coolshell.cn/asyncjs/alert.js';
    loadjs(script);

這個方式幾乎成了標準的非同步載入js檔案的方式。

按需非同步載入js

上面的那個DOM方式的例子解決了非同步載入javascript的問題,但是沒有解決我們想讓它按照我們指定的時機執行的問題。所以,我們只需要把上面的那個DOM方式幫到某個事件上來就可以了。

比如:

1) 綁在window.load 事件上(示例)

window.load = loadjs("http://coolshell.cn/asyncjs/alert.js")

2) 綁在特定的事件上(示例)

<p style="cursor: pointer" onclick="LoadJS()">Click to load alert.js </p>

比如當我們在點選某個DOM元素時,才載入我們的JS檔案。

更多

但是,繫結在某個特定事件上這個事似乎又過了點,因此只有在點選的時候才會去真正的下載js,這又會太慢了。,我們想要非同步地把嘉實穩鍵下載到本地,但又不執行,僅當在我們想要執行的時候去執行。

要是我們又下面的方式就好了:


    var script = document.createElement("script");
    script.noexecute = true;
    script.src = "alert.js";
    document.body.appendChild(script);

    //後面我們可以這麼幹
    script.execute();

可惜的是,這只是一個美麗的夢境,今天我們的javascript還比較原始,這個js夢還沒有實現。

所以,我們的程式設計師只能是喲個hack的方式來搞。

有的程式設計師使用非標準的script的type來實現。如:

    <script type=cache/script src="./alert.js"></script>

因為“cache/script”,這個東西根本就不能被瀏覽器歇息,所以瀏覽器也就不能把alert.jsjavascript執行,但是它又要去下載嘉實穩鍵,所以就可以搞定了。可惜的是,webkit嚴格服從了HTML標準–對於這個不認識的東西,直接刪除,什麼也不幹,於是我們的夢又破了。

所以,我們需要在hack以下,就是preload圖片那樣,我們可以動用object標籤(也可以使用iframe標籤),於是我們有了下面這樣的程式碼:

    function cachejs(script_filename){
        var cache = document.createElement('object');
        cache.data = script_filename;
        cache.id = "coolshell_script_cache_id";
        cache.width = 0;
        cache.height = 0;
        document.body.appendChild(cache);
    }

示例

在Chrome下按Ctrl+Shift+I。切換到network頁,你可以看到下載了alert.js但是沒有執行,因為瀏覽器快取了,不會再從伺服器上下載alert.js,所以,就能保證執行速度了。

最後在提兩個js,一個事ControlJs,另一個是HeadJs都是專門用來做非同步load javascript檔案的。

相關文章