前端模塊化詳解

模塊化開發方便代碼的管理,提升代碼複用性,下降代碼耦合,每一個模塊都會有本身的做用域。當前流行的模塊化規範有CommonJS,AMD,CMD,ES6的import/export前端

  • CommonJS的主要實踐者就是nodejs,通常對模塊輸出用module.exports去輸出,用require去引入模塊,CommonJS通常採用同步加載【require / module.exports / exports】
  • AMD聽從RequireJs規範,推崇依賴前置(提早執行)【require / defined】
  • CMD聽從SeaJs規範,推崇依賴就近(延遲執行)【require / defined】
  • ES6 可靜態分析,提早編譯,不是在運行時確認【import / export】

發展歷程

簡單封裝 -> 命名空間/modules -> script loader/modules loader -> 同步加載CommonJS/AMD/CMD -> ES6 import export/export default -> 模塊打包工具browserify/webpacknode

簡單封裝

用法:把不一樣的函數簡單地放在一塊兒,看做一個模塊
缺點:jquery

  • "污染"了全局變量,沒法保證不與其餘模塊發生變量名衝突;
  • 模塊成員之間看不出直接關係

對象(命名空間)

用法:把功能代碼放入對象中,看成對象的屬性
優勢:減小了全局上的變量數目,避免變量全局污染
缺點:本質是對象,而這個對象會暴露全部模塊成員,內部狀態能夠被外部改寫webpack

當即執行函數(IIFE)

  1. 聲明一個匿名函數
  2. 立刻調用這個匿名函數

做用:建立一個獨立的做用域。數據是私有的, 外部只能經過暴露的方法操做
這個做用域裏面的變量,外面訪問不到(即避免「變量污染」)es6

CommonJS

導入導出:require & exports/module.exports
主要實踐者:NodeJSweb

模塊導入
語法:require(module)數組

eg:
var math = require('math');
math.add(2, 3);

若是模塊輸出的是一個函數,那就不能定義在exports對象上面,而要定義在module.exports變量上面。瀏覽器

// example2.js
module.exports = function () {
    console.log("hello world")
}
        
// main.js 
require('./example2.js')()

AMD(Asynchronous Module Definition)

提倡依賴前置,在定義模塊的時候就要聲明其依賴的模塊。在requireJs推廣過程當中產生的規範服務器

模塊定義
define(id?, dependencies?, factory)
id:字符串,模塊名稱(可選)
dependencies:數組,是咱們要載入的依賴模塊(可選),使用相對路徑
factory:工廠方法,返回一個模塊函數babel

模塊導入
require([module], callback)
module:是一個數組,裏面的成員就是要加載的模塊
callback:則是加載成功以後的回調函數

requireJS 優勢

  • 實現js文件的異步加載,避免網頁失去響應;
  • 管理模塊之間的依賴性,按照依賴關係加載,便於代碼的編寫和維護。

採用異步方式加載模塊,經過define來定義一個模塊,經過require來引入模塊,模塊的加載不影響後面語句的執行,全部依賴於這些模塊的語句都寫在一個回調函數中,加載完畢後,這個回調函數才運行

CMD(Common Module Definition)

提倡就近依賴(按需加載),在用到某個模塊的時候再去require進來。在Sea.js推廣過程當中產生的規範

模塊定義
define(id?, dependencies?, factory) 與AMD相似

// eg
define('hello', ['jquery'], function(require, exports, module) {
   // 模塊代碼
   // return 模塊對象
});

模塊導入導出
與 AMD 相似

ES6

導入導出:import/export/export default

export defalut

默認輸出是一個函數/變量
其餘模塊加載該模塊時,import命令能夠爲該匿名函數指定任意名字
須要注意的是,這時import命令後面,不使用大括號

// export-fn.js 
export default function () {
    console.log('foo');
}

import foo from 'export-fn.js'

export default命令也能夠用在非匿名函數前,視同匿名函數加載

export default 命令用於指定模塊的默認輸出。一個模塊只能有一個默認輸出,所以export default 命令只能使用一次。因此,import命令後面纔不用加大括號,由於只可能惟一對應export default命令。
本質上,export default就是輸出一個叫作default的變量或方法,而後系統容許你爲它取任意名字

export & export default區別

使用export default時,對應的import語句不須要使用大括號,默認輸出
使用export時,對應的import語句須要使用大括號

// 第一組 export default
export default function crc32() { // 輸出
    // ...
}
import crc32 from 'crc32'; // 輸入


// 第二組 export 
export function crc32() { // 輸出
    // ...
};
import {crc32} from 'crc32'; // 輸入

ES六、AMD和CommonJS區別

ES6模塊使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。
CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西;
CommonJS 模塊就是對象,輸入時必須查找對象屬性

  • CommonJS模塊

    let { stat, exists, readFile } = require('fs')

    總體加載fs模塊(即加載fs的全部方法),生成一個對象(_fs),而後再從這個對象上面讀取 3 個方法。這種加載稱爲「運行時加載」

  • ES6模塊

    import { stat, exists, readFile } from 'fs'

    從fs模塊加載 3 個方法,其餘方法不加載,這種加載稱爲「編譯時加載」或者靜態加載

  • CommonJS模塊

    const path = './' + fileName
    const myModual = require(path)

    動態加載,require到底加載哪個模塊,只有運行時才知道

import()

動態加載,正在提案階段,import()返回一個 Promise 對象
import()加載模塊成功之後,這個模塊會做爲一個對象,看成then方法的參數
import()相似於 Node 的require方法,區別主要是前者是異步加載,後者是同步加載。

適用場景
(1)按需加載
(2)條件加載
(3)動態的模塊路徑

注:關於ES6模塊化,詳細見 阮一峯的es6入門 module模塊

es6 與 commonJS/AMD 模塊化區別

  • 模塊化的規範:CommonJS和AMD兩種。前者用於服務器,後者用於瀏覽器。
  • 而ES6 中提供了簡單的模塊系統,徹底能夠取代現有的CommonJS和AMD規範,成爲瀏覽器和服務器通用的模塊解決方案。
  • ES6 模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西。

require與import的區別(commonJS與ES6模塊化區別)

require/exports是CommonJS的一部分;import/export是ES6的新規範
require支持 動態導入,import不支持,正在提案 (babel 下可支持)
require是 同步 導入,import屬於 異步 導入
require是 值拷貝,導出值變化不會影響導入值;import是值引用,指向 內存地址,導入值會隨導出值而變化

參考:
前端工程師必備:前端的模塊化
阮一峯的es6入門 module模塊

相關文章
相關標籤/搜索