10分鐘帶你瞭解JavaScript模塊化的前世今生!

導語:本文帶你去了解一下JavaScript模塊化的前世今生,包括但不限於JavaScript模塊化、模塊化規範、模塊加載器和模塊打包工具等。本文不是一個深度剖析JavaScript模塊相關話題的文章,僅是一個能夠讓你10分鐘快速瞭解JavaScript模塊化相關知識的介紹。

作者徐江偉--騰訊前端工程師

@IMWeb前端社區



現在JavaScript技術的發展可能會讓你應接不暇。身爲一線搬磚工的你對雨後春筍般的前端工具和框架越來越疲於學習和暇接。有時候,你可能不自主的問,webpack是什麼玩意?browserify又是什麼東東?AMD和CommonJS這都是啥,又有啥關係和區別?

模塊化是什麼?

我們首先看一下模塊化的定義是什麼,以下定義來自百度百科:

模塊化:

模塊化是指解決一個複雜問題時自頂向下逐層把系統劃分成若干模塊的過程,有多種屬性,分別反映其內部特性。

乍看起來有點高深,簡單地講就是模塊化可以讓我們將代碼分功能和業務等維度來切分成小的組織單元,然後根據需要來更好的組織代碼,增加代碼的可維護性、複用性等。其實,確切的講,我們寫代碼完全可以不用模塊化。但正如提到的,模塊化可以顯著地增加代碼的複用性、可維護性和擴展性等優點,是自上世紀開始寫代碼後人們在實踐總結出來的優秀實踐原則。

具體到JavaScript中來講,模塊化帶來了以下幾點優點:

  • 代碼抽象:將可複用的代碼邏輯抽象到通用的庫,屏蔽實現的複雜性;

  • 代碼封裝:將代碼的實現封裝到模塊裏,對外暴露公用的API,從而使調用模塊不必關心實現的複雜性;

  • 代碼複用:也即是DRY( Dont Repeat Yourself)原則,減少代碼的冗餘,增加代碼的可複用性;

  • 代碼管理:模塊化後的代碼更加易於組織代碼結構和管理。

ES5之前的模塊化

遺憾的講,在ES5版本和之前的版本中,JavaScript並沒有模塊化的概念。行業優秀的工程師們通過各種各樣的方式來模擬JavaScript的模塊化。來,下面我們就看看常用的兩種模擬方法是怎麼樣的。

1、立即執行函數

立即執行函數,英文爲Immediately Invoked Function Expression,簡稱爲IIFE。通常結構如下:

(function(){  // ...})()

從中可以看出,IIFE是一個一旦被聲明就會被立即執行的匿名函數。那麼匿名的立即執行函數帶來了哪些特點呢:

  • 將邏輯的複雜性實現都封裝在IIFE函數內;

  • 由於function會隔離作用域,因此IIFE內聲明的任何變量都會被當做IIFE的局部變量,從而不會污染到全局變量。

因此,我們可以說,IIFE爲一個小的模塊。但是,從中我們可以看出,它並沒有提供良好的依賴管理的實現。

2、模塊化模式

模塊化的模式是在立即執行函數上更進了一步,我們將需要提供給第三方的變量return出來,如下:

// 將封裝的模塊賦值給一個變量
 var helloApi = function(){  
 // 內部實現  function sayHello(){    
     console.log('Hello');  }  
 // 對外暴露API  return {      sayHello: sayHello  } }()


然後,我們可以按照如下方式來調用模塊的API了:

helloApi.sayHello();  // Hello

以上,實現了簡單的代碼模塊化。隨着JavaScript模塊化實踐的不斷推進,企業和社區冒出了很多不同的模塊化定義和使用的庫,如seaJS、moduleJS、requireJS等,各自語法大同小異,也各有優缺點。

模塊化規範

模塊化的規範定義了我們如何來寫模塊化的代碼。其實,在ES6發佈之前,JavaScript語言並沒有推出官方的規範來確定模塊化定義語法。上面我們提到了,JavaScript模塊化實踐的不斷推進,企業和社區冒出了很多不同的模塊化定義和使用的庫,因此也有不同的規範版本。包括ES6等一些比較優秀的規範包括:

  • Asynchronous Module Definition (AMD)

  • Common Module Definition (CMD)

  • CommonJS

  • Universal Module Definition (UMD)

  • ES6 module format

我們挨個看看,每一個是如何來定義和實現模塊化的。

1、Asynchronous Module Definition (AMD)

AMD 是 RequireJS 在推廣過程中對模塊定義的規範化產出的。看一下AMD的語法:

define(['./a', './b'], function(a, b) {  
   // 依賴必須一開始就寫好    a.doSomething();    
   // 此處略去 100 行    b.doSomething();    ... })

2、Common Module Definition (CMD)

CMD是seaJS在推廣過程中對模塊化定義的規範產出的。與上面的例子類似,我們看看CMD的語法:

define(function(require, exports, module) {   
    var a = require('./a')  ;
    a.doSomething()  ;    
   var b = require('./b');
   // 依賴可以就近書寫      b.doSomething(); })

3、CommonJS

CommonJS是在Nodejs中定義模塊中使用非常廣泛的規範,它使用require和module.exports定義模塊的依賴和API導出,簡單的例子如下:

var dep1 = require('./dep1');  
var dep2 = require('./dep2');
module.exports = function(){    // ...
}

4、Universal Module Definition (UMD)

UMD基本上是統一了瀏覽器端和後端JS(Nodejs)的模塊化定義,看下它的定義便可窺知:圖片5、ES6 module

ES6的發佈基本上第一次在JavaScript語言層面增加了對模塊化的支持,它使用了import和export來定義依賴和模塊導出。看一個簡單的例子:

// hello.js
// 導出sayHello方法
 export function sayHello(){    console.log('Hello'); }
// other.js
// 引入hello模塊的sayHello方法
 import { sayHello } from './hello';  sayHello(); // => Hello

模塊加載器

說白了,模塊加載器來解釋和加載模塊化的代碼。它的職責包括:

  • 加載並解釋主引導main.js或者app.js;

  • 根據需要加載其它需要的模塊化js文件。

國內用的比較多的包括requireJS、moduleJS、seaJS等。特別需要說明的一點是,模塊加載器是執行在瀏覽器端,需要加載到瀏覽器裏,在webapp運行階段執行。

模塊打包器

模塊化打包器或者說模塊化打包工具的出現,基本上說可以替代了模塊加載器,可以完全將模塊加載器的事都給幹了。但是,有一點與模塊加載器不同的是,它運行在webapp構建階段。它的特點如下:

  • 在運行階段,將各類依賴s文件構建爲一個單獨的js文件,如app.js或者build.js;

  • 瀏覽器只需要加載build.js或者app.js這個單一文件即可。

所有的文件都在構建的時候打包爲一個文件,瀏覽器端只加載這一個文件完全夠了。比較火的模塊打包工具包括:webpack和browserify等。

總結

到這裏,相信你對JavaScript的模塊化的前世今生和關鍵的名詞都比較瞭解了。這裏給出各個概念的解釋:

  • 模塊: 模塊是指將一段功能代碼的具體實現封裝在單獨的模塊當中,並對外暴露出可供調用的API,從而方便其它模塊加載和調用;

  • 模塊化規範:模塊化規範是定義瞭如何定義和使用模塊的語法;

  • 模塊加載器:模塊加載器用來加載和解釋由遵循特定規範定義的模塊,要注意的是在瀏覽器端執行;

  • 模塊打包器:模塊打包器用來在構建階段替代模塊加載器的工作並且構建出唯一的js文件的工具,要注意的是在構建階段執行。


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