模塊化總結(CommonJS/AMD/CMD/ES6模塊化)

背景前提:很久之前,開發網頁要通過命名空間的方式來組織代碼,例如jQuery庫把它的API都放在了window.$下,在加載完jQuery後其他模塊再通過window.$去使用jQuery。但是這樣做存在的問題,包括:

  • 命名空間衝突,兩個庫可以會使用同一個名稱(例:Zepto也放在window.$下)
  • 無法合理地管理項目的依賴和版本
  • 無法方便地控制依賴的加載順序

模塊化是指把一個複雜的系統分解成多個模塊以方便編碼。

模塊化的開發方式可以提高代碼複用率,方便進行代碼的管理。通常一個文件就是一個模塊,有自己的作用域,只向外暴露特定的變量和函數。

目前流行的js模塊化規範有CommonJS、AMD、CMD以及ES6模塊化

CommonJS

1、CommonJS是一種使用廣泛的JavaScript模塊化規範。 CommonJS 規範的流行得益於 Node.js 採用了這種方式,後來這種方式被引入到了網頁開發中。

2、核心思想:通過require方法同步加載依賴其他模塊,通過module.exports導出

// 定義模塊moduleA
function add(a,b) {
    return a + b
}

// 導出
module.exports = moduleA.add;

// 導入
const moduleA = require('./moduleA');
moduleA.add(2,3)

3、優缺點

優點:

  • 代碼可複用於 Node.js 環境下並運行,例如做同構應用;
  • 通過 NPM 發佈的很多第三方模塊都採用了 CommonJS 規範。

缺點

  • 無法直接運行在瀏覽器環境下,必須通過工具轉換成標準的 ES5
  • 在服務端,模塊文件都存在本地磁盤,讀取非常快,所以這樣做不會有問題。但是在瀏覽器端,要從服務器端加載模塊,這時就必須採用非同步模式

AMD

1、AMD 也是一種 JavaScript 模塊化規範,與 CommonJS 最大的不同在於它採用異步的方式去加載依賴的模塊。AMD 規範主要是爲了解決針對瀏覽器環境的模塊化問題,最具代表性的實現是 requirejs.

2、用require.config()指定引用路徑等,用define()定義模塊,用require()加載模塊。

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

// 定義一個模塊module
define('module', ['dep'], function(dep) {
  return exports;
});


// 導入和使用
require(['module'], function(module) {
});

3、優缺點

優點

  • 可在不轉換代碼的情況下直接在瀏覽器中運行
  • 可異步加載依賴
  • 可並行加載多個依賴
  • 代碼可運行在瀏覽器環境和 Node.js 環境下

缺點

  • JavaScript 運行環境沒有原生支持 AMD,需要先導入實現了 AMD 的庫後才能正常使用。

CMD

// 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();
    }
});

與AMD類似,不同點:AMD 推崇依賴前置、提前執行,CMD推崇依賴就近、延遲執行

ES6 模塊化

// 導入
import { readFile } from 'fs';
import React from 'react';
// 導出
export function hello() {};
export default {
  // ...
};

缺點在於目前無法直接運行在大部分 JavaScript 運行環境下,必須通過工具轉換成標準的 ES5 後才能正常運行。

特點:

  • 靜態化,必須在頂部,不能使用條件語句,自動採用嚴格模式
  • treeshaking和編譯優化,以及webpack3中的作用域提升
  • 外部可以拿到實時值,而非緩存值(是引用而不是copy)

ES6 模塊與 CommonJS 模塊的區別

不同點:

1、CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。

  • CommonJS 模塊輸出的是值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。
  • ES6 模塊的運行機制與 CommonJS 不一樣。JS 引擎對腳本靜態分析的時候,遇到模塊加載命令import,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被加載的那個模塊裏面去取值。換句話說,ES6 的import有點像 Unix 系統的“符號連接”,原始值變了,import加載的值也會跟着變。因此,ES6 模塊是動態引用,並且不會緩存值,模塊裏面的變量綁定其所在的模塊。

2、CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。

  • 運行時加載: CommonJS 模塊就是對象;即在輸入時是先加載整個模塊,生成一個對象,然後再從這個對象上面讀取方法,這種加載稱爲“運行時加載”。
  • 編譯時加載: ES6 模塊不是對象,而是通過 export 命令顯式指定輸出的代碼,import時採用靜態命令的形式。即在import時可以指定加載某個輸出值,而不是加載整個模塊,這種加載稱爲“編譯時加載”。

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

相同點:兩者都可以對對象內部屬性的值進行改變

  • 即使我們使用了 ES6 的模塊系統,如果藉助 Babel 的轉換,ES6 的模塊系統最終還是會轉換成 CommonJS 的規範。
  • Babel5 中使用 require 時,引入值是 module.export 返回的值或者是 export default 返回的值。
  • Babel6中,使用 import 引入時,可以直接獲取到 export default 的值 ; 但是如果是 require 導入的組件, 無論導出是 module.export 、export 、 export default可以直接獲取到 export default 的值都必須要加上一個 default。
     

 

參考文章:1、https://webpack.wuhaolin.cn/1%E5%85%A5%E9%97%A8/1-1%E5%89%8D%E7%AB%AF%E7%9A%84%E5%8F%91%E5%B1%95.html

2、https://es6.ruanyifeng.com/#docs/module-loader#ES6-%E6%A8%A1%E5%9D%97%E4%B8%8E-CommonJS-%E6%A8%A1%E5%9D%97%E7%9A%84%E5%B7%AE%E5%BC%82

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