前端模塊化CommonJS、CMD、AMD、ES6

一、CommonJS

????????Node.js是commonJS規(guī)范的主要實(shí)踐者,它有四個(gè)重要的環(huán)境變量為模塊化的實(shí)現(xiàn)提供支持:module、exports、require、global。實(shí)際使用時(shí),用module.exports定義當(dāng)前模塊對(duì)外輸出的接口(不推薦直接用exports),用require加載模塊。

  // 定義模塊math.js
  var basicNum = 0;

  function add(a, b) {
    return a + b;
  }
  module.exports = { //在這里寫上需要向外暴露的函數(shù)、變量
    add: add,
    basicNum: basicNum
  }

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

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

????????commonJS用同步的方式加載模塊。在服務(wù)端,模塊文件都存在本地磁盤,讀取非??欤赃@樣做不會(huì)有問題。但是在瀏覽器端,限于網(wǎng)絡(luò)原因,更合理的方案是使用異步加載。

二、AMD和require.js

????????AMD規(guī)范采用異步方式加載模塊,模塊的加載不影響它后面語句的運(yùn)行,所有依賴這個(gè)模塊的語句,都定義在一個(gè)回調(diào)函數(shù)中,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行,這里介紹用require.js實(shí)現(xiàn)AMD規(guī)范的模塊化,用require.config()指定引用路徑等,用define()定義模塊,用require()加載模塊;
????????首先我們需要引入require.js文件和一個(gè)入口文件main.js,main.js中配置require.config()并規(guī)定項(xiàng)目中用到的基礎(chǔ)模塊;

/** 網(wǎng)頁中引入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",  //實(shí)際路徑為js/lib/jquery.min.js
    "underscore": "underscore.min",
  }
});
// 執(zhí)行基本操作
// 引用模塊的時(shí)候,我們將模塊名放在[]中作為reqiure()的第一參數(shù);如果我們定義的模塊本身也依賴其他模塊,那就需要將它們放在[]中作為define()的第一參數(shù);
require(["jquery","underscore"],function($,_){
  // some code here
});
// 定義math.js模塊
define(function () {
    var basicNum = 0;
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
        basicNum :basicNum
    };
});
// 定義一個(gè)依賴underscore.js的模塊
define(['underscore'],function(_){
  var classify = function(list){
    _.countBy(list,function(num){
      return num > 30 ? 'old' : 'young';
    })
  };
  return {
    classify :classify
  };
})
 
// 引用模塊,將模塊放在[]內(nèi)
require(['jquery', 'math'],function($, math){
  var sum = math.add(10,20);
  $("#sum").html(sum);
});

三、CMD

????????require.js在申明依賴的模塊時(shí)會(huì)在第一之間加載并執(zhí)行模塊內(nèi)的代碼:CMD是另一種js模塊化方案,它與AMD很類似,不同點(diǎn)在于:AMD 推崇依賴前置、提前執(zhí)行,CMD推崇依賴就近、延遲執(zhí)行。此規(guī)范其實(shí)是在sea.js推廣過程中產(chǎn)生的。

/** AMD寫法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
     // 等于在最前面聲明并初始化了要用到的所有模塊
    a.doSomething();
    if (false) {
        // 即便沒用到某個(gè)模塊 b,但 b 還是提前執(zhí)行了
        b.doSomething()
    } 
});
 
/** CMD寫法 **/
define(function(require, exports, module) {
    var a = require('./a'); //在需要時(shí)申明
    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

????????ES6在語言標(biāo)準(zhǔn)的層面上,實(shí)現(xiàn)了模塊功能,而且實(shí)現(xiàn)得相當(dāng)簡(jiǎn)單,旨在成為瀏覽器和服務(wù)器通用的模板解決方案,其模塊功能主要由兩個(gè)命令構(gòu)成:export和import,export命令用于規(guī)定模塊的對(duì)外接口,import命令用于輸入其他模塊提供的功能;使用import命令的時(shí)候,用戶需要知道所要加載的變量名或函數(shù)名,其實(shí)ES6還提供了export default命令,為模塊指定默認(rèn)輸出,對(duì)應(yīng)的import語句不需要使用大括號(hào),這也更趨近于ADM的引用寫法;ES6的模塊不是對(duì)象,import命令會(huì)被 JavaScript 引擎靜態(tài)分析,在編譯時(shí)就引入模塊代碼,而不是在代碼運(yùn)行時(shí)加載,所以無法實(shí)現(xiàn)條件加載。也正因?yàn)檫@個(gè),使得靜態(tài)分析成為可能;

/** 定義模塊 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);
}
/** export default **/
//定義輸出
export default { basicNum, add };
//引入
import math from './math';
function test(ele) {
    ele.textContent = math.add(99 + math.basicNum);
}

五、 ES6 模塊與 CommonJS 模塊的差異

1. CommonJS 模塊輸出的是一個(gè)值的拷貝,ES6 模塊輸出的是值的引用。

  • CommonJS 模塊輸出的是值的拷貝,也就是說,一旦輸出一個(gè)值,模塊內(nèi)部的變化就影響不到這個(gè)值。
  • ES6 模塊的運(yùn)行機(jī)制與 CommonJS 不一樣。JS 引擎對(duì)腳本靜態(tài)分析的時(shí)候,遇到模塊加載命令import,就會(huì)生成一個(gè)只讀引用。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用,到被加載的那個(gè)模塊里面去取值。換句話說,ES6 的import有點(diǎn)像 Unix 系統(tǒng)的“符號(hào)連接”,原始值變了,import加載的值也會(huì)跟著變。因此,ES6 模塊是動(dòng)態(tài)引用,并且不會(huì)緩存值,模塊里面的變量綁定其所在的模塊。

2. CommonJS 模塊是運(yùn)行時(shí)加載,ES6 模塊是編譯時(shí)輸出接口。

  • 運(yùn)行時(shí)加載: CommonJS 模塊就是對(duì)象;即在輸入時(shí)是先加載整個(gè)模塊,生成一個(gè)對(duì)象,然后再?gòu)倪@個(gè)對(duì)象上面讀取方法,這種加載稱為“運(yùn)行時(shí)加載”。

  • 編譯時(shí)加載: ES6 模塊不是對(duì)象,而是通過 export 命令顯式指定輸出的代碼,import時(shí)采用靜態(tài)命令的形式。即在import時(shí)可以指定加載某個(gè)輸出值,而不是加載整個(gè)模塊,這種加載稱為“編譯時(shí)加載”。

CommonJS 加載的是一個(gè)對(duì)象(即module.exports屬性),該對(duì)象只有在腳本運(yùn)行完才會(huì)生成。而 ES6 模塊不是對(duì)象,它的對(duì)外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會(huì)生成

參考鏈接:前端模塊化:CommonJS,AMD,CMD,ES6

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容