簡單的js異步文件加載器

咱們常常會遇到這種場景,某些頁面依賴第三方的插件,而這些插件比較大,不適合打包到頁面的主js裏(假設咱們使用的是cmd的方式,js會打包成一個文件),那麼這個時候咱們一般會異步獲取這些插件文件,並在下載完成後完成初始化的邏輯。javascript

以圖片上傳爲例,咱們可能會用到plupload.js這個插件,那麼咱們會這麼寫:vue

!window.plupload ?
        $.getScript( "/assets/plupload/plupload.full.min.js", function() {
            self._initUploader();
        }) :
        self._initUploader();

可是咱們的頁面一般由多個獨立的模塊(組件)構成,若是頁面上的A、B兩個模塊都依賴於plupload.js, 那是否是在兩個地方都要把上面的代碼寫一遍。若是這麼幹,在plupload.js被下載下來以前,可能會發起兩個請求,因爲是並行下載,js文件可能會被重複下載,而不是第一次下載下來,第二次取緩存的內容。java

下圖是頁面多個組件依賴vue.js的狀況(jquery和vue混用的場景):jquery

圖片描述

因此,在實際使用中須要加鎖,即當腳本正在加載時,不該該再重複請求腳本,等待加載完成後,依次執行後面的邏輯,有promise這個好工具,實現起來很簡單。promise

// vue加載器
var promiseStack = [];
function loadvue() {
    var promise = $.Deferred();
    if (loadvue.lock) {
        promiseStack.push(promise);
        
    } else {
        loadvue.lock = true;
        window.Vue ? 
             promise.resolve() : // 這裏寫錯了,window.Vue爲true的時候lock要置爲false,我在後面改過來了
             $.getScript( "/assets/vue/vue.min.js", function() {
                loadvue.lock = false;
                promise.resolve();
                promiseStack.forEach(function(prom) {
                    prom.resolve();
                });
            });
        
    }
    return promise;
}
window.loadvue = loadvue;

而後在依賴vue.js地方:緩存

loadvue().then(function() { // do something });

再看看請求:異步

圖片描述

好了,到這裏彷佛解決問題了,可是假如個人頁面上存在多個插件依賴,好比既依賴plupload.js又依賴vue.js,難道我要把上面的代碼再寫一遍(怎麼感受好像說過這話)?這樣不就冗餘了麼?因此咱們須要一個異步加載器的生成器,可以幫助咱們生成多個異步加載器。工具

/**
 * @des: js異步加載器生產器
 * @param {string} name   加載器名稱
 * @param {string} global 全局變量
 * @param {string} url    加載地址
 **/

var promiseStack = {};

exports.generate = function(name, global, url) {
    var foo = function() {
        if (!promiseStack[name]) {
            promiseStack[name] = [];
        }
        var promise = $.Deferred();
        if (foo.lock) {
            promiseStack[name].push(promise);
            
        } else {
            foo.lock = true;
            if (window[global]) {
                foo.lock = false;
                promise.resolve();
            } else {
                $.getScript(url, function() {
                    foo.lock = false;
                    promise.resolve();
                    promiseStack[name].forEach(function(prom) {
                        prom.resolve();
                    });
                });
            }
            
        }
        return promise;
    };
    
    return foo;
};

而後咱們能夠生成異步加載器並賦給windowurl

// 全局加載器
window.loadvue = loader.generate(
'vue', 
'Vue', 
'/assets/vue/vue.min.js');
window.loadPlupload = loader.generate(
'plupload', 
'plupload', 
'/assets/plupload/plupload.full.min.js');

使用的時候同上,這樣就基本解決了咱們的問題。因此,我想說的是抽象思惟很重要。spa

相關文章
相關標籤/搜索