鍍金池/ 教程/ 嵌入式/ cordova.js 模塊系統(tǒng) require/define
Cordova CLI
CordovaLib 概要
cordova.js 概要
cordova.js 事件通道 pub/sub
cordova.js 模塊系統(tǒng) require/define
cordova.js 導(dǎo)入、初始化、啟動、加載插件
cordova.js 本地交互 JS<->Native

cordova.js 模塊系統(tǒng) require/define

類似于 Java 的 package/import,在 JavaScript 中也有類似的 define/require,它用來異步加載 module 化的 js,從而提高運行效率。

  • define 定義注冊一個 module
  • require 加載使用一個 module

模塊化加載的必要性,起源于 nodejs 的出現(xiàn)。但是 JavaScript 并沒有內(nèi)置模塊系統(tǒng),所以就出現(xiàn)了很多規(guī)范。 主要有2種:CommonJSAMD(Asynchronous Module Definition)。還有國內(nèi)興起的 CMD(Common Module Definition)

CommonJS 主要面對的是服務(wù)器,代表是 Node.js;AMD 針對瀏覽器進(jìn)行了優(yōu)化,主要實現(xiàn) require.js;CMD 是 seajs。

cordova-js 最開始采用的是 require.js 作者寫的 almond.js(兼容 AMD 和 CommonJS),但之后由于特殊需求(比如模塊不存在的時候要 throw 異常),最終從 almond.js fork 過來實現(xiàn)了一個簡易 CommonJS 風(fēng)格的模塊系統(tǒng),同時提供了和 nodejs 之間很好的交互。在 cordova.js 中可以直接使用 define()和 require(),在其他文件可以通過 cordova.define()和 cordova.require()來調(diào)用。所以 src/scripts/require.js 中定義的就是一個精簡的 JavaScript 模塊系統(tǒng)。

源碼如下:

Js 代碼

// file: src/scripts/require.js

// 定義2個cordova.js內(nèi)部使用的全局函數(shù)require/define
var require,
    define;

// 通過自調(diào)用的匿名函數(shù)來實例化全局函數(shù)require/define
(function () {
    // 全部模塊
    var modules = {},
    // 正在build中的模塊ID的棧
        requireStack = [],
    // 標(biāo)示正在build中模塊ID的Map
        inProgressModules = {},
        SEPARATOR = ".";

    // 模塊build
    function build(module) {
        // 備份工廠方法
        var factory = module.factory,
        // 對require對象進(jìn)行特殊處理
            localRequire = function (id) {
                var resultantId = id;
                if (id.charAt(0) === ".") {
                    resultantId = module.id.slice(0, module.id.lastIndexOf(SEPARATOR)) + SEPARATOR + id.slice(2);
                }
                return require(resultantId);
            };
        // 給模塊定義一個空的exports對象,防止工廠類方法中的空引用
        module.exports = {};
        // 刪除工廠方法
        delete module.factory;
        // 調(diào)用備份的工廠方法(參數(shù)必須是require,exports,module)
        factory(localRequire, module.exports, module);
        // 返回工廠方法中實現(xiàn)的module.exports對象
        return module.exports;
    }

    // 加載模塊
    require = function (id) {
        // 如果模塊不存在拋出異常
        if (!modules[id]) {
            throw "module " + id + " not found";
        // 如果模塊正在build中拋出異常
        } else if (id in inProgressModules) {
            var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
            throw "Cycle in require graph: " + cycle;
        }
        // 如果模塊存在工廠方法說明還未進(jìn)行build(require嵌套)
        if (modules[id].factory) {
            try {
                // 標(biāo)示該模塊正在build
                inProgressModules[id] = requireStack.length;
                // 將該模塊壓入請求棧
                requireStack.push(id);
                // 模塊build,成功后返回module.exports
                return build(modules[id]);
            } finally {
                // build完成后刪除當(dāng)前請求
                delete inProgressModules[id];
                requireStack.pop();
            }
        }
        // build完的模塊直接返回module.exports
        return modules[id].exports;
    };

    // 定義模塊
    define = function (id, factory) {
        // 如果已經(jīng)存在拋出異常
        if (modules[id]) {
            throw "module " + id + " already defined";
        }
        // 模塊以ID為索引包含ID和工廠方法
        modules[id] = {
            id: id,
            factory: factory
        };
    };

    // 移除模塊
    define.remove = function (id) {
        delete modules[id];
    };

    // 返回所有模塊
    define.moduleMap = modules;
})();

// 如果處于nodejs環(huán)境的話,把require/define暴露給外部
if (typeof module === "object" && typeof require === "function") {
    module.exports.require = require;
    module.exports.define = define;
}

其中 factory(localRequire, module.exports, module);
第一個參數(shù)“l(fā)ocalRequire”實質(zhì)還是調(diào)用全局的 require()函數(shù),只是把 ID 稍微加工了一下支持相對路徑。cordova.js 沒有用到相對路徑的 require,但在一些 Plugin 的 js 中有,比如 Contact.js 中 ContactError = require('./ContactError');

不知道什么原因要把 module.exports 單做為工廠方法的第兩個參數(shù),cordova.js 中除了個別地方用到第二個參數(shù)(base64.js、uilder.js、modulemapper.js、pluginloader.js、utils.js),大部分都是在用第三個參數(shù)在工廠方法里調(diào)用 module.exports。Plugin 的 js 代碼中也基本是 module.exports。

參考:
https://github.com/seajs/seajs/issues/588
http://phonegap.com/2012/03/21/introducing-cordova-js/

上一篇:CordovaLib 概要下一篇:Cordova CLI