五分鐘帶你回顧前端模塊化發展史

五分鐘帶你回顧前端模塊化發展史

CSS 早在 2.1 的版本就提出了 @import 來實現模塊化,可是 JavaScript 直到 ES6 纔出現官方的模塊化方案 ES Module。儘管早期 JavaScript 語言規範上不支持模塊化,但這並無阻止 JavaScript 的發展。官方沒有模塊化標準,那麼咱們就本身動手建立標準。社區裏的前輩們建立並實現了規範,這些規範即是前端模塊化發展之路上智慧的結晶。javascript

模塊化的價值

2020 年的今天來看,模塊化應該具備如下價值:前端

  • 可維護性
  • 減小全局污染
  • 可複用性
  • 方便管理依賴關係
  • 分治思想的實踐

十年以前,模塊化還只是使用閉包簡單的實現一個命名空間。使用這種解決方式能夠簡單粗暴的處理全局變量和依賴關係等問題。java

關於閉包的概念能夠參考個人另外一篇文章node

CommonJS

讓咱們把時間追溯到 2009 年 1 月,在這個普通的冬天裏,萬物開始生長。中國南極科學考察站崑崙站落成,成爲南極海拔最高的科學考察站。在 JavaScript 社區中, Mozilla 的工程師 Kevin Dangoor 發起了 CommonJS 的提案。(最初叫ServerJS)git

再到後來橫空出世的 Node.js 採用 CommonJS 模塊化規範,同時還帶來了 npm (全球最大的模塊倉庫) 。程序員

nodejs 中模塊的實現並不是徹底按照 CommonJS 的規範來的,而是進行了取捨,並增長了少量特性。es6

CommonJS 規範在服務端表現出色,使得 JavaScript 在服務器端大放異彩,與傳統服務器語言(PHP、Python)等產生抗衡甚至壓倒之勢。程序員們便萌發出了將它移植到瀏覽器端的想法。github

然而因爲CommonJS的模塊加載是同步的。咱們知道,服務器端加載的模塊從內存或磁盤中加載,耗時基本可忽略。可是在瀏覽器端卻會形成阻塞,白屏時間過長,用戶體驗不夠友好。npm

所以,從CommonJS中逐漸產生了一些分支,也就是業內熟知的AMDCMD等。瀏覽器

AMD規範

James Burke提出了 AMD 規範,RequireJS 也是他的表明做。他同時開發了 amdefine(在node中可使用AMD規範的庫)。

AMD 主要是爲了解決 CommonJS 規範在瀏覽器端的不足:

  • 缺乏模塊封裝的能力
  • 使用同步的方式加載依賴
  • export 只能導出變量,導出函數須要用 module.export (這一般不符合直覺)

AMD 規範定義了一個 define 全局方法用來定義和加載模塊,RequireJS 後期也擴展了 require 全局方法用來加載模塊 。其核心實現是內部的模塊加載器。

CMD規範

@玉伯提出了 sea.js (CMD規範的實現)。

準確的說 CMDSeaJS 在推廣過程當中對模塊定義的規範化產物。

相比於AMD的異步加載,CMD更傾向於懶加載,規範自己也與CommonJS更貼近。

由於是懶加載機制,因此 sea.js 提供了 seajs.use 方法,來運行已經定義的模塊。全部 define 的回調函數都不會當即執行,而是將全部的回調函數進行緩存,只有 use 以後,以及被 require 的模塊回調纔會執行。

RequireJS 和 sea.js 的區別

  • sea.js 只有在 require 的地方,纔會真正執行模塊。
  • RequireJS 會先運行全部的依賴,獲得全部的結果後再執行回調。

AMD 和 CMD 區別

  • 對於依賴的模塊,AMD 是提早執行,CMD 是延遲執行。
  • CMD 推崇依賴就近,AMD 推崇依賴前置。
  • AMDAPI 一個當多個用,職責單一。CMD 中,每一個API都簡單純粹。

來自@玉伯的回答

UMD

UMDAMDCommonJS 的綜合產物。AMD 用於瀏覽器,CommonJS 用於服務器。UMD 則是二者的兼容模式,解決了跨平臺問題。

實現原理:if-else

詳情請移步githubUMD

ES Module

天下大勢,分久必合。

十年以後,官方爸爸推出的 ES6 模塊化方案,一統瀏覽器和服務器。採用了徹底靜態化的方式進行模塊加載。

語法

模塊導入

// 默認導入default模塊
// main.js
import name from './module.js'

// module.js
const name = '前端食堂'
export default name
// 若是想導入其餘模塊
// main.js
import { name, getName } from './module.js'

// module.js
export const name = '前端食堂'
export const getName = () => name
// 同時導入
// main.js
import  name, { getName } from './module.js'

// module.js
const name = '前端食堂'
export const getName = () => name
export default name
// 重命名
// main.js
import * as mod from './module.js'
let name = ''
name = mod.name
name = mod.getName()

// module.js
export const name = '前端食堂'
export const getName = () => name
// 對單獨的變量進行重命名
import { name, getName as getModName }

模塊導出

// 第一種寫法
export const name = '前端食堂'

// 第二種寫法
export function getName() {
    return name
}
export class Logger {
    log(...args) {
      console.log(...args)
    }
}

// 第三種寫法
const name = '前端食堂'
function getName() {
    return name
}
class Logger {
    log(...args) {
      console.log(...args)
    }
}
export {name, getName, Logger}

// 第四種寫法
const name = '前端食堂'
export default name

這裏只提供了一些基本語法,更多語法請參考阮一峯老師的 《ECMAScript 6 入門》

固然,不一樣的規範之中,被規範的語法也有所不一樣,這裏推薦業內公認的語法規範airbnb

這裏列舉出幾個airbnb中模塊導出/引入的規範。

1.若是模塊只有一個輸出值,就使用 export default ,若是模塊有多個輸出值,就不使用 export defaultexport default 與普通的 export 不要同時使用。

2.模塊導入時不要使用通配符。由於這樣能夠確保你的模塊之中,有一個默認輸出 (export default)

// bad
import * as myObject from './importModule';

// good
import myObject from './importModule';

ES Module與CommonJS的差別

1.語法 import/export require/module

2.CommonJS 模塊輸出的是一個值的拷貝(不會隨原始值變化),ES6 模塊輸出的是值的引用(會隨着原始值變化)。

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

寫在最後

整天討論這門語言好,或者那門語言壞的人,甚至是可悲的。既悲其一葉障目,更悲其大愚若智的自得心態。 ——《大道至簡》

JavaScrit 至今仍有被人詬病的缺憾之處。但歷史告訴咱們,再多的阻力也阻擋不了真正熱愛它的人們。

感謝爲 JavaScript 模塊化之路貢獻的開發者們。

愛心三連擊

1.看到這裏了就點個「贊」支持下吧,你的是我創做的動力。

2.關注公衆號「前端食堂」,你的前端食堂,記得按時吃飯!

3.特殊階段,帶好口罩,作好我的防禦。

公衆號二維碼.jpg

相關文章
相關標籤/搜索