來!咱們一塊兒來從頭開始構建本身的JavaScript模塊化工具

前言

但願編寫程序能像玩積木同樣,首先規劃要產出怎樣的做品,而後在積木堆中挑選合適的積木塊,最後一組合就完工了。html

因而JavaScript須要相似這樣模塊化,每一個模塊都隱藏內部細節而且對外暴露接口,再處理好模塊之間的依賴關係,就能夠達到玩積木的效果了。git

雖然現有不少模塊化框架/工具,但對於新手來講,爲何不本身擼一個簡單的模塊化工具呢?github

但願經過這個工具把本身以爲好用的代碼以模塊的方式組織起來,漸漸造成本身的JS庫,以後能夠勇敢地和HR說,本身的小項目用的是本身小JS庫,^_^。我以爲,在這個封裝的過程當中,新手能學習到不少東西。gulp

新手嘛,多造輪子老是有好處的,=_=。segmentfault

從閉包到模塊

如下是《你所不知道的JavaScript(上卷)》中對於閉包的說明。數組

當函數能夠記住並訪問所在的詞法做用域,即便函數是在當前詞法做用域以外執行,這時就產生了閉包。閉包

其實,無論怎樣,閉包正如其字面意思同樣,既能提供一個相對封閉的空間,也能向外界暴露必要的接口。這不就正符合咱們模塊化的需求嗎?app

在此,建議參考這篇文章,以增強您對閉包的理解:《假如技術HR問您JavaScript的「閉包」,嘿嘿嘿,舉這個例子就夠了》框架

最簡單的模塊

var test = (function test(){
    function run(){
        console.log("run test");
    }
    return {
        run: run
    };
})();
test.run();

上面的代碼有個叫test的函數做爲模塊建立器,每次調用它均可以建立一個新的模塊。這裏使用當即執行函數,當即建立了一個test模塊。參考閉包的概念,外部能夠調用test模塊中的run函數,同時test模塊又有本身獨立的做用域。能達到一個積木塊(模塊)的要求。模塊化

簡單的模塊這樣寫沒有問題,可是模塊間的依賴問題沒有解決。

最簡單的模塊管理工具

模塊之間必然會存在依賴關係,而模塊管理工具須要可以很好地管理模塊間的依賴。下面咱們模仿實現了AMD規範的工具requirejs,主要是模仿其define,get的API風格,本身寫一個簡單的版本。

//模塊管理工具,MyModules
var MyModules = (function Manager() {
    var modules = {};
    function define(name, deps, impl) {
        for (var i=0; i<deps.length; i++){
            //將依賴的名字替換成已經註冊了的模塊
            deps[i] = modules[deps[i]];
        }
        //將依賴數組展開成參數傳入模塊的構建函數,生成新模塊
        modules[name] = impl.apply(impl, deps);
    }
    function get(name){
        return modules[name];
    }
    return {
        define: define,
        get: get
    }
})();
//定義一個模塊,data
MyModules.define("data",[],function(){
    var name = "miku";
    function getName(){
        return name;
    }
    return {
        getName:getName
    }
});
//定義一個模塊,app
//該模塊依賴data模塊
MyModules.define("app", ["data"], function(data){
    function run(){
        console.log(data.getName());
    }
    return {
        run:run
    }
});
//取出app模塊
var app = MyModules.get("app");
//調用app模塊的run方法
app.run();

能夠看出,利用MyModules能夠很方便地定義使用模塊,管理模塊依賴。可是還存在一個問題,MyModules對於模塊定義的順序有要求。以上面的例子來講,就是app模塊依賴data模塊,那data模塊必須在app模塊以前被定義。這個限制讓咱們實際使用中不是很方便。接下來咱們將改進它。

改進模塊管理工具

咱們須要讓模塊管理工具不須要限制模塊的定義順序,這裏個人作法是,使用一個rebuilds數組來保存未成功構建的模塊。每次有新模塊構建成功的時候就會從新嘗試去構建整個rebuilds數組中的模塊。具體看下面的代碼。

window.mm_modules = (function Manager() {
    var debug = false;
    var modules = {};
    var rebuilds = [];
    function copyArray (array){
        var tempArray = [];
        for(var i=0; i<array.length; i++){
            tempArray.push(array[i]);
        }
        return tempArray;
    }
    function define(name, deps, impl) {
        //判斷依賴構建是否成功
        var depsDone = true;
        
        //拷貝一份當前想要構建的模塊,當構建失敗時使用
        var rebuildCopy = {
            name : name,
            deps : copyArray(deps),
            impl : impl
        };
        
        //循環依賴數組,構建依賴
        for (var i=0; i<deps.length; i++){
            //將依賴的名字替換成已經註冊了的模塊
            deps[i] = modules[deps[i]];
            //有依賴的模塊未定義,因此這個模塊構建失敗
            if(!deps[i]){
                depsDone = false;
                break;
            }
        }
        
        //若是依賴構建成功,即模塊構建成功
        if(depsDone){
            //將依賴數組展開成參數傳入模塊的構建函數,生成新模塊
            modules[name] = impl.apply(impl, deps);
            //從rebuilds數組中移除
            if(rebuilds[name]){
               delete rebuilds[name];
            }
            //循環rebuilds數組,嘗試重新構建以前構建失敗的模塊
            for (key in rebuilds){
                var rebuild = rebuilds[key];
                if(rebuild){
                    //遞歸調用
                    define(rebuild.name, rebuild.deps, rebuild.impl);
                }
            }
        }
        //模塊構建失敗,存入rebuilds數組中,等待下一次從新構建
        else{
            rebuilds[name] = rebuildCopy;
        }
        if(debug){
            console.log("[mm_modules debug]need rebuild modules:", rebuilds);
        }
    }
    function get(name){
        return modules[name];
    }
    return {
        define: define,
        get: get
    }
})();

改進後的模塊管理工具,可以自動地處理模塊依賴,而不須要限制定義順序了。
那,能不能更進一步呢?試着想一下,咱們平常會怎麼使用?單文件單模塊,而後把這些文件放在不一樣文件夾裏組織好。因而,我就想到使用gulp這樣的工具輔助咱們。

gulp輔助

請參考下面的目錄結構。

├── dist
│   ├── index.html
│   └── js
│       └── mm-modules-build.js
├── gulpfile.js
├── mm-modules
│   ├── queryObject.js
│   ├── request.js
│   ├── template.js
│   ├── test.js
│   └── util.js
├── mm-modules.js

能夠在mm-modules下隨意地定義模塊,如util模塊內有各類工具函數,template模塊則包含了artTemplate模版引擎。以後利用gulp將mm-modules.js(模塊管理工具)與mm-modules下全部的模塊文件打包成mm-modules-build.js。項目中只要引入mm-modules-build.js便可。

結尾

到此,咱們本身構建了一個很實用的JavaScript模塊化工具,項目的源碼在這裏:https://github.com/MIKUScallion/mm-modules,喜歡的話,給個✨。

再回顧一下前言的話。

但願經過這個工具把本身以爲好用的代碼以模塊的方式組織起來,漸漸造成本身的JS庫,以後能夠勇敢地和HR說,本身的小項目用的是本身小JS庫,^_^。我以爲,在這個封裝的過程當中,新手能學習到不少東西。

新手嘛,多造輪子老是有好處的,=_=。

參考

  • 《你所不知道的JavaScript(上卷)》

相關文章
相關標籤/搜索