Javascript模塊化編程——使用AMD,CommonJS,ES Harmony

定一個小目標,每週翻譯一篇國外優秀博客
原文鏈接 https://addyosmani.com/writing-modular-js/
以及參考了 http://nuysoft.com/2014/01/24/authoring-umd-modules/
http://www.ruanyifeng.com/blog/2012/11/require_js.html

AMD-異步模塊規範

個人理解:
首先AMD模式,所謂異步模塊加載。
就是先加載數組裏面得模塊,然後等到加載完畢,才執行匿名函數。

隨着RequireJS成爲最流行的實現方式,異步模塊規範(AMD)在前端界已經被廣泛認同。
它採用異步方式加載模塊,模塊的加載不影響它後面語句的運行。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數纔會運行。

    //    文件名: foo.js
    define(["jquery"], function ($) {
        //    方法
        function myFunc(){};

        //    暴露公共方法
        return myFunc;
    });

AMD也採用require()語句加載模塊,但是不同於CommonJS,它要求兩個

require([module], callback);參數[module]

第一個參數[module],是一個數組,裏面的成員就是要加載的模塊;第二個參數callback,則是加載成功之後的回調函數。如果將前面的代碼改寫成AMD形式,就是下面這樣:

 require(['math'], function (math) {
    math.add(2, 3);
  });

CommonJS

個人理解:
commonJS的規範是用nodeJS引出來的。
但是瀏覽器前端是不能直接用的,需要有browserify。

因爲有Browserify,它也一直被前端界廣泛認同。
就像前面的格式一樣,下面是用CommonJS規範實現的foo模塊的寫法:

//    文件名: foo.js
    //    依賴
    var $ = require("jquery");
    //    方法
    function myFunc(){};

    //    暴露公共方法(一個)
    module.exports = myFunc;

requireJS

爲什麼要使用requireJS

最早的時候,所有Javascript代碼都寫在一個文件裏面,只要加載這一個文件就夠了。後來,代碼越來越多,一個文件不夠了,必須分成多個文件,依次加載。下面的網頁代碼,相信很多人都見過。

  <script src="1.js"></script>
  <script src="2.js"></script>
  <script src="3.js"></script>
  <script src="4.js"></script>
  <script src="5.js"></script>
  <script src="6.js"></script>

首先需要去官網下載requireJS

下載後,假定把它放在js子目錄下面,就可以加載了。

 <script src="js/require.js"></script>

UMD模式

然後我現在要學習的就是這個模式。

如果是在瀏覽器中運行代碼,那麼 AMD 模塊 是個非常好的選擇。
如果運行在服務端環境,例如 RingoJS 或 node.js,那麼 CommonJS 模塊 是最簡單的選擇。

一個AMD包裹爲UMD的例子

(function (define) {

    // dependencies are listed in the dependency array
    define(['./store', 'meld'], function (store, meld) {
        "use strict";
        var cache = {};

        // create the module
        meld.around(store, 'get', function (jp) {
            var key = jp.args.join('|');
            return key in cache ? cache[key] : cache[key] = jp.proceed();
        };

        // return your module's exports
        return store;
    });

}(
    typeof define == 'function' && define.amd
        ? define
        : function (ids, factory) {
            var deps = ids.map(function (id) { return require(id); });
            module.exports = factory.apply(null, deps);
        }
));

整個模塊被包裹在一個 IIFE 中,並且函數 define 被作爲一個參數傳入。
文件最後的代碼片段 typeof define == ‘function’ && define.amd 是嗅探 AMD 環境的標準方式。
如果檢測結果爲 true,–define 則說明當前環境是 AMD,可以把全局函數 define 傳入 IIFE。通過由*工廠函數返回一個值,模塊以正常的 AMD 方式輸出。

如果 AMD 環境嗅探的結果爲 false,代碼則模擬一個類似 node.js 的 CommonJS 環境。爲了使 AMD 代碼能夠運行,IIFE 注入了一個行爲類似於 AMD define 的函數:把所有的 ids 加載爲模塊,並把它們作爲參數注入工廠函數。然後,函數 define 獲取到工廠函數的返回值,並以經典的 node.js 方式賦值給 module.exports。

感覺下面的例子好理解一點:

(function (root, factory) {
        if (typeof define === "function" && define.amd) {
            // AMD
            define(["jquery"], factory);
        } else if (typeof exports === "object") {
            // Node, CommonJS之類的
            module.exports = factory(require("jquery"));
        } else {
            // 瀏覽器全局變量(root 即 window)
            root.returnExports = factory(root.jQuery);
        }
    }(this, function ($) {
        //    方法
        function myFunc(){};

        //    暴露公共方法
        return myFunc;
    }));

下面是更復雜的例子,它依賴了多個組件並且暴露多個方法:

 (function (root, factory) {
        if (typeof define === "function" && define.amd) {
            // AMD
            define(["jquery", "underscore"], factory);
        } else if (typeof exports === "object") {
            // Node, CommonJS之類的
            module.exports = factory(require("jquery"), require("underscore"));
        } else {
            // 瀏覽器全局變量(root 即 window)
            root.returnExports = factory(root.jQuery, root._);
        }
    }(this, function ($, _) {
        //    方法
        function a(){};    //    私有方法,因爲它沒被返回 (見下面)
        function b(){};    //    公共方法,因爲被返回了
        function c(){};    //    公共方法,因爲被返回了

        //    暴露公共方法
        return {
            b: b,
            c: c
        }

一個用UMD模式寫的print.js

(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        //檢測是否爲AMD模式
        define([], factory);//這裏沒有引用包或者庫,第一個數組爲空
    } else {
     // 瀏覽器全局變量(root 即 window)
        root.Printer = factory(root);
    }
}(this, function(root) {
    var Printer = {};
    Printer.printer = {
        "version": "0.0.1"
    };
    var init_options = {
        "speed": 50, //文字的速度
        "selector": 'canvas', //要打印到的標籤的ID          
        "startIndex": 0, //從第幾個字符開始打印
        "endIndex": 0, //打印到第幾個字符結束
        "hasCur": true, //是否有光標
        "curId": 'cur', //光標的ID
        "curStr": '_', //光標字符
        "curStyle": 'font-weight: bold;', //光標的樣式(CSS樣式)
        "curSpeed": 100, //光標的速度(ms)
        "lnStr": ""
    };

    var str = "",
        options = init_options;
    var flwCurTimer, dom, curObj, reStr = '',
        curSwitch, index = 0;

    Printer.init = function(arg_str, arg_options) {
        console.log('init');
        console.log(arg_str);
        console.log(arg_options);
        str = arg_str;
        for (var option in arg_options) {
            options[option] = arg_options[option];
        }
        dom = document.getElementById(options.selector);
        index = options.startIndex;
        options.endIndex = options.endIndex == 0 ? str.length : options.endIndex
        options.hasCur && flwCur();
        return this;
    }


    Printer.print = function() { //打印函數
        console.log('print');
        for (var i = 0; i < str.length; i++) {
            (function(index) {
                setTimeout(function() {
                    console.log('index: ' + index);
                    if (str.charAt(index) === '\n') {
                        reStr += '<br>' + options.lnStr;
                    } else {
                        reStr += str.charAt(index);
                    }
                    dom.innerHTML = options.lnStr + reStr;
                    // console.log(reStr);
                    // console.log(dom.innerHTML);
                }, options.speed * (index + 1))
            })(i);
        }

        setTimeout(function() {
            if (options.hasCur) {
                var element = document.createElement("span");
                element.id = options.curId
                dom.appendChild(element);

                curObj = document.getElementById(options.curId);
                clearTimeout(flwCurTimer);
                setInterval(chCur, options.curSpeed);
            }
        }, options.speed * str.length)
    }

    function flwCur() { //跟隨光標
        console.log('flwCur');
        dom.innerHTML += '<span id="' + options.curId + '" style="' + options.curStyle + '">' + options.curStr + '</span>';
        flwCurTimer = setTimeout(flwCur, 1.5 * options.speed);
    }

    function chCur() { //閃爍光標
        // console.log('chCur');
        curObj.innerHTML = curSwitch ? options.curStr : "";
        curSwitch = !curSwitch
    }

    return Printer;//Printer是公有方法
}));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章