JS指令碼載入後執行相應回撥函式

singcl發表於2019-03-04

問題

專案中經常會遇到這樣的問題:當某個js指令碼載入完成後再執行相應任務,但很多朋友可能並不知道怎麼判斷我們要載入的js檔案是否載入完成,如果沒有載入完成我們就呼叫js檔案裡面的函式是不會成功的。本文主要講解怎麼在成功載入js檔案後再執行相應回撥任務。

基本思路

我們可以動態的建立 <script> 元素,然後通過更改它的src 屬性來載入指令碼,但是怎麼知道這個指令碼檔案載入完成了呢?因為有些函式需要在指令碼載入完成才能呼叫。IE 瀏覽器中可以使用 <script> 元素的 onreadystatechange 來監控載入狀態的改變,並通過判斷它的 readyStateloadedcomplete 來判斷指令碼是否載入完成。而非 IE 瀏覽器可以使用 onload 來直接判斷指令碼是否載入完成。

動態指令碼簡單示例

一個簡單的實現過程如下:

// IE下:
var HEAD = document.getElementsByTagName(`head`)[0] || document.documentElement
var src = `http://xxxxxx.com`
var script = document.createElement(`script`)
script.setAttribute(`type`,`text/javascript`)
script.onreadystatechange = function() {
    if(this.readyState === `loaded` || this.readyState === `complete`) {
        console.log(`載入成功!`)
    }
}
script.setAttribute(`src`, src)
HEAD.appendChild(script)
複製程式碼
// Chrome等現代瀏覽器:
var HEAD = document.getElementsByTagName(`head`)[0] || document.documentElement;
var src = `http://xxxxxx.com`
var script = document.createElement(`script`)
script.setAttribute(`type`,`text/javascript`)
script.onload = function() {
    console.log(`載入成功!`)
}
script.setAttribute(`src`, src)
HEAD.appendChild(script)
複製程式碼

原理很簡單,根據這兩個簡單的原理,我們進行一些修改,我把改成了兩個函式,分別是序列載入並行載入

序列和並行動態指令碼

當傳一個包含多個JS檔案路徑的陣列時,序列載入函式從第一個指令碼檔案載入開始,每載入成功一個便開始載入下一個指令碼檔案,全部載入完成後執行回撥函式。而並行載入是一開始便載入全部的指令碼檔案,也就是他們從同一點開始載入,當全部載入完成後,執行回撥函式。

/** 
 * 序列載入指定的指令碼
 * 序列載入[非同步]逐個載入,每個載入完成後載入下一個
 * 全部載入完成後執行回撥
 * @param {Array|String}  scripts 指定要載入的指令碼
 * @param {Function} callback 成功後回撥的函式
 * @return {Array} 所有生成的指令碼元素物件陣列
 */

function seriesLoadScripts(scripts, callback) {
    if(typeof(scripts) !== `object`) {
        var scripts = [scripts];
    }
    var HEAD = document.getElementsByTagName(`head`)[0] || document.documentElement;
    var s = [];
    var last = scripts.length - 1;
    //遞迴
    var recursiveLoad = function(i) {
        s[i] = document.createElement(`script`);
        s[i].setAttribute(`type`,`text/javascript`);
        // Attach handlers for all browsers
        // 非同步
        s[i].onload = s[i].onreadystatechange = function() {
            if(!/*@cc_on!@*/0 || this.readyState === `loaded` || this.readyState === `complete`) {
                this.onload = this.onreadystatechange = null; 
                this.parentNode.removeChild(this);
                if(i !== last) {
                    recursiveLoad(i + 1);
                } else if (typeof(callback) === `function`) {
                    callback()
                };
            }
        }
        // 同步
        s[i].setAttribute(`src`, scripts[i]);
        HEAD.appendChild(s[i]);
    };
    recursiveLoad(0);
}
 

/**
 * 並行載入指定的指令碼
 * 並行載入[同步]同時載入,不管上個是否載入完成,直接載入全部
 * 全部載入完成後執行回撥
 * @param {Array|String}  scripts 指定要載入的指令碼
 * @param {Function} callback 成功後回撥的函式
 * @return {Array} 所有生成的指令碼元素物件陣列
 */ 

function parallelLoadScripts(scripts, callback) {
    if(typeof(scripts) !== `object`) {
        var scripts = [scripts];
    }
    var HEAD = document.getElementsByTagName(`head`)[0] || document.documentElement;
    var s = [];
    var loaded = 0;
    for(var i = 0; i < scripts.length; i++) {
        s[i] = document.createElement(`script`);
        s[i].setAttribute(`type`,`text/javascript`);
        // Attach handlers for all browsers
        // 非同步
        s[i].onload = s[i].onreadystatechange = function() {
            if(!/*@cc_on!@*/0 || this.readyState === `loaded` || this.readyState === `complete`) {
                loaded++;
                this.onload = this.onreadystatechange = null;
                this.parentNode.removeChild(this);
                if(loaded === scripts.length && typeof(callback) === `function`) callback();
            }
        };
        // 同步
        s[i].setAttribute(`src`,scripts[i]);
        HEAD.appendChild(s[i]);
    }
}
複製程式碼

在這裡是把 <script> 標籤動態的插入到頁面中的 <head> 標籤內部,並且載入完成後標籤元素會被自動移除。

使用方法

這裡宣告瞭一個陣列變數,裡面包含了兩個遠端的JS檔案地址(當然 <script> 標籤呼叫指令碼是支援跨域的):

var scripts = [  
    "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
    "http://wellstyled.com/files/jquery.debug/jquery.debug.js"
];

// 這兩個檔案分別是 jQuery 1.4.的庫檔案和 jQuery Debug 外掛
// 然後你可以使用下面的方法呼叫並在成功後執行回撥了。

parallelLoadScripts(scripts, function() {  
   /*
   debug = new $.debug({  
       posTo : { x:`right`, y:`bottom` },
       width: `480px`,
       height: `50%`,
       itemDivider : `<hr>`,
       listDOM : `all`
   });
   */
   console.log(`指令碼載入完成啦`);
});
複製程式碼

相關文章