javascript 非同步模組載入 簡易實現

帥蟲哥發表於2015-09-18

  在javascript是沒有類似java或其他語言的模組概念的,因此也不可能通過import或using等關鍵字來引用模組,這樣造成了複雜專案中前端程式碼混亂,變數互相影響等。

  因此在複雜專案中引入AMD的概念,AMD:全稱是Asynchronous Module Definition,即非同步模組載入機制。通過AMD可以不需要在頁面中手動新增<script>來引用指令碼,而通過定義依賴自動載入模組指令碼,接下來的程式碼將講解如何實現建議的AMD模組,如果需要檢視比較詳細的實現可以下載requirejs原始碼。

簡易實現整體思路:

 

1.將模組名及模組檔案地址存入記憶體

2.通過define方法將模組名及模組依賴關係以及模組實現程式碼存入記憶體

3.require方法通過loadscript方法將需要依賴的模組程式碼匯入並執行define方法存入記憶體,模組通過入參傳入實際程式碼中,從而完成模組載入。

 

1.定義模組:

實現模組的定義,並將模組定義儲存。

           /**
           * 
           * @param id 模組名
             * @param deps 依賴模組
             * @param factory 模組實現
             */
            define: function (id, deps, factory) {

 

在定義模組中,需要將模組名,模組依賴,模組程式碼儲存至記憶體中

           /**
           * 
           * @param id 模組名
             * @param deps 依賴模組
             * @param factory 模組實現
             */
            define: function (id, deps, factory) {
                // 模組是否存在
                if (modules[id]) throw new Error("module:" + id + "已經存在!");
                if (arguments.length > 2) {
                    modules[id] = {
                        id: id,
                        deps: deps,
                        factory: factory
                    }
                }
                else if (arguments.length == 2) {
                    modules[id] = {
                        id: id,
                        factory: deps
                    }
                }
                else {
                    throw new Error("module:引數有誤!");
                }
            },

2.引用模組:

輸入依賴模組名,執行程式碼,程式碼示例如下:

           /**
            * 非同步匯入模組
             * @param deps 依賴模組
             * @param callback
            * @returns {{}}
            */
            require: function (deps, callback) {
                // 插入指令碼
                deps = [].slice.call(deps);
                // 獲取依賴指令碼
                loadScript(deps, buildModule, callback);
            },

詳細步驟:

首先需要從依賴的資料夾中匯入指令碼,

        /**
        * 從外部載入js
        * @param deps 依賴模組
         * @param buildModule 建立模組方法
         * @param callback
        */
        function loadScript(deps, buildModule, callback) {
            var depJsCounter = 0;
            deps.forEach(function (dep) {
                var script = document.createElement("script")
                script.type = "text/javascript";
                script.src = configs[dep];
                document.getElementsByTagName("head")[0].appendChild(script);
                script.onload = function () {
                    // 依賴js載入計數標記
                    depJsCounter++;
                    if (depJsCounter == deps.length) {
                        buildModule(deps, callback)();
                    }
                };
            });

        }

構建模組,從全域性module中取出依賴模組,將依賴模組作為入參注入到現有模組,最後執行現有模組

        /**
         * 建立模組
         * @param deps 依賴模組
         * @param callback
         * @returns {Function}
         */
        var buildModule = function (deps, callback) {
            return function () {
                // 獲取依賴模組
                var args = [];
                deps = [].slice.call(deps);
                deps.forEach(function (dep) {
                    var module = modules[dep]
                    if (typeof module.factory === 'function')
                        module.factory = module.factory();
                    args.push(module.factory)
                })
                // 將依賴模組注入到callback中
                var temp = {};
                callback.apply(temp, args);
                return temp
            }
        }

3.註冊模組

註冊模組主要將名字與檔案路徑關聯起來,便於從路徑中下載js,程式碼清單如下:

           /**
             * 註冊模組
             * @param obj
             */
            config: function (obj) {
                var path = obj.paths;
                for (var el in path) {
                    Object.defineProperty(configs, el, {
                        enumerable: false,
                        configurable: false,
                        writable: true,
                        value: path[el]
                    })
                }
            }
        }

 

4.執行例項

編寫模組

define('module1', function () {
    var module1 = {};
    module1.sayhello = function () {
        return 'hello module1';
    }
    return module1;
});

將程式碼儲存js檔案並取名module1

新建html檔案,在html中先註冊模組,通過require 匯入模組,並利用模組編寫程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="/demo/01Requirejs/require.js"></script>
</head>
<body>

</body>
</html>
<script>

require.config({
   paths:{
       module1:'/demo/01Requirejs/module1.js'
   }
});

require(['module1'],function(module1){
    alert(module1.sayhello());
})

</script>

相關文章