1. 程式人生 > >前端模塊化方案:CommonJS/AMD/CMD/ES6規範

前端模塊化方案:CommonJS/AMD/CMD/ES6規範

node.js 定義 兩個 glob export nbsp listen 代碼復用 www.

模塊化的開發方式可以提高代碼復用率,方便進行代碼的管理。通常一個文件就是一個模塊,有自己的作用域,只向外暴露特定的變量和函數。目前流行的js模塊化規範有CommonJS、AMD、CMD以及ES6的模塊系統。前端模塊化的演進過程可以看看阮一峰老師的文章。本文主要介紹各規範下的模塊化實現方式。

一、CommonJS

Node.js是commonJS規範的主要實踐者,它有四個重要的環境變量為模塊化的實現提供支持:module、exports、require、global。實際使用時,用module.exports定義當前模塊對外輸出的接口(不推薦直接用exports),用require加載模塊。

//定義模塊math.js
var basicNum = 0;
function add(a, b) {
    return a + b;
}
module.exports = { //在這裏寫上需要向外暴露的函數、變量
    add: add,
    basicNum: basicNum
}

//引用自定義的模塊時,參數包含路徑,可省略.js
var math = require(‘./math‘);
math.add(2, 5);

//引用核心模塊時,不需要帶路徑
var http = require(‘http‘);
http.createService(...).listen(3000);

  commonJS用同步的方式加載模塊。在服務端,模塊文件都存在本地磁盤,讀取非常快,所以這樣做不會有問題。但是在瀏覽器端,限於網絡原因,更合理的方案是使用異步加載。

二、AMD和require.js

  AMD規範采用異步方式加載模塊,模塊的加載不影響它後面語句的運行。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數才會運行。這裏介紹用require.js實現AMD規範的模塊化:用require.config()指定引用路徑等,用define()定義模塊,用require()加載模塊。

  首先我們需要引入require.js文件和一個入口文件main.js。main.js中配置require.config()並規定項目中用到的基礎模塊。

/** 網頁中引入require.js及main.js **/
<script src="js/require.js" data-main="js/main"></script>

/** main.js 入口文件/主模塊 **/
//首先用config()指定各模塊路徑和引用名
require.config({
    baseUrl: "js/lib",
    paths: {
        "jquery": "jquery.min",  //實際路徑為js/lib/jquery.min.js
        "underscore": "underscore.min",
    }
});
//執行基本操作
require(["jquery","underscore"],function($,_){
    // some code here
});

  引用模塊的時候,我們將模塊名放在[]中作為reqiure()的第一參數;如果我們定義的模塊本身也依賴其他模塊,那就需要將它們放在[]中作為define()的第一參數。

//定義math.js模塊
define(function () {
    var basicNum = 0;
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
        basicNum :basicNum
    };
});
//定義一個依賴underscore.js的模塊
define([‘underscore‘],function(_){
    var classify = function(list){
        _.countBy(list,function(num){
            return num >30 ? ‘old‘: ‘young‘;
        })
    };
    return {
        classify :classify
    };
})

//引用模塊,將模塊放在[]內
require([jquery,math],function($,math){
    var sum = math.add(10,20);
    $("#sum").html(sum);
});

三、CMD和sea.js

  require.js在申明依賴的模塊時會在第一之間加載並執行模塊內的代碼:

define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
     // 等於在最前面聲明並初始化了要用到的所有模塊
    if (false) {
        // 即便沒用到某個模塊 b,但 b 還是提前執行了
        b.foo()
    } 
});

  CMD是另一種js模塊化方案,它與AMD很類似,不同點在於:AMD 推崇依賴前置、提前執行,CMD推崇依賴就近、延遲執行。此規範其實是在sea.js推廣過程中產生的。

/** AMD寫法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
     // 等於在最前面聲明並初始化了要用到的所有模塊
    a.doSomething();
    if (false) {
        // 即便沒用到某個模塊 b,但 b 還是提前執行了
        b.doSomething()
    } 
});

/** CMD寫法 **/
define(function(require, exports, module) {
    var a = require(‘./a‘); //在需要時申明
    a.doSomething();
    if (false) {
        var b = require(‘./b‘);
        b.doSomething();
    }
});

/** sea.js **/
// 定義模塊 math.js
define(function(require, exports, module) {
    var $ = require(‘jquery.js‘);
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});
// 加載模塊
seajs.use([‘math.js‘], function(math){
    var sum = math.add(1+2);
});

四、ES6 Module

  ES6 在語言標準的層面上,實現了模塊功能,而且實現得相當簡單,旨在成為瀏覽器和服務器通用的模塊解決方案。其模塊功能主要由兩個命令構成:exportimportexport命令用於規定模塊的對外接口,import命令用於輸入其他模塊提供的功能。

/** 定義模塊 math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

/** 引用模塊 **/
import { basicNum, add } from ‘./math‘;
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

  如上例所示,使用import命令的時候,用戶需要知道所要加載的變量名或函數名。其實ES6還提供了export default命令,為模塊指定默認輸出,對應的import語句不需要使用大括號。這也更趨近於ADM的引用寫法。

/** export default **/
//定義輸出
export default { basicNum, add };
//引入
import math from ‘./math‘;
function test(ele) {
    ele.textContent = math.add(99 + math.basicNum);
}

  ES6的模塊不是對象,import命令會被 JavaScript 引擎靜態分析,在編譯時就引入模塊代碼,而不是在代碼運行時加載,所以無法實現條件加載。也正因為這個,使得靜態分析成為可能。

前端模塊化方案:CommonJS/AMD/CMD/ES6規範