不論是前端老司機仍是剛接觸前端的"菜鳥"。模塊化想必在天天工做中,或多或少都會接觸到。尤爲針對一些針對React
、Vue
開發的同窗來講,那就是天天都會脫口而出的一個必備術語。而且在不少技術文檔中,都經常看到AMD
、UMD
、COMMONJS
還有ES6中的module
。javascript
可是,模塊化的本質是什麼!前端是如何從"茹毛飲血"的<script>
到如今es6的module
的呢。html
今天咱們就來嘮嘮這段不爲人知的故事。前端
何爲模塊化。其實就是功能的單一化或者說功能的切片化編程。更直白一點就是,每個獨立的功能都有本身獨立的做用域。java
讓咱們看看針對模塊的英文定義es6
Modules are an integral piece of any robust application's architecture and typically help in keeping the units of code for a project both cleanly separated and organized.編程
而JS在實現模塊代碼有以下方式:設計模式
讓咱們針對每個方式來一一說明api
因爲JS語法自己沒有塊級做用域的概念(es6以前),因此是無法直接利用{}
來將指定的代碼進行封裝。若是想將特定用於處理相似功能的代碼合併到一塊兒。對象字面量不失爲一個很好的方式。(有人會說,用函數封裝也能夠啊,記住JS中一切皆對象)瀏覽器
var myModule = {
myProperty: "北宸",
// 對象字面量能夠包含屬性和方法
// 咱們還能夠爲該模塊定義配置信息:
myConfig: {
useCaching: true,
language: "en"
},
//
saySomething: function () {
console.log( "你好啊,世界" );
},
// 基於配置信息輸出一些信息
reportMyConfig: function () {
console.log( "緩存: " + ( this.myConfig.useCaching ? "可用" : "禁用") );
},
// 從新配置信息
updateMyConfig: function( newConfig ) {
if ( typeof newConfig === "object" ) {
this.myConfig = newConfig;
console.log( this.myConfig.language );
}
}
};
// 你好啊,世界
myModule.saySomething();
// 緩存可用
myModule.reportMyConfig();
// fr
myModule.updateMyConfig({
language: "fr",
useCaching: false
});
// 緩存禁用
myModule.reportMyConfig();
複製代碼
從上述代碼中,能夠看到,將一些操做和數據進行了封裝。實現了,功能切片化處理。可是若是細想,感受利用字面量來封裝數據和方法,感受有點雞肋。由於這個對象是一個單例。若是隻是在一個地方使用和操做,那徹底沒有問題,可是若是是多個地方都用到呢,同時多個地方都須要對指定的屬性進行修改。這就是牽一髮而動全身的操做。緩存
同時,咱們能夠看到利用字面量進行數據和方法封裝。這些屬性都是對外友好的。都是能在外部訪問到的。沒有絲毫的隱私,用傳統OOP編程術語來說。這些屬性都是public
的。也就是說,沒法實現屬性私有化。
爲了解決字面量沒法進行屬性私有化。模塊模式應用而生。這也是JS語言在早起比較經常使用的模塊化處理方式。
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
複製代碼
從代碼上看到,一個IIFE赫然映入眼簾。偷偷的告訴你們,模塊模式
就是利用IIFE
實現的。
爲了避免佔用很大篇幅來說解這個實現。特定爲你們準備了飯後甜點。JS_Module模式深刻了解一下
其實AMD(Asynchronous Module Definition)是一種爲瀏覽器環境書寫模塊的模式。 而可以實現異步加載的關鍵就在於RequireJS。
RequireJS
是在ES6module
沒出現以前,經常使用的前端模塊解決方案。RequireJS
將加載的每個獨立模塊做爲<script>
,並利用head.appendChild()
追加到文檔中。
RequireJS等待全部依賴模塊加載,將該模塊須要的額外模塊進行排序,並在依賴模塊加載完以後,調用本模塊的定義函數。
在項目中存在以下結構,咱們用cart.js
、inventory.js
來構建一個shirt.js
define(["./cart", "./inventory"], function(cart, inventory) {
//返回一個對象用於定義"my/shirt"模塊
return {
color: "blue",
size: "large",
addToCart: function() {
inventory.decrement(this);
cart.add(this);
}
}
}
);
複製代碼
固然,上述中的本地模塊cart.js
也能夠換成JQuery
等現成的模塊。
若是想對AMD
有一個更深的瞭解,或者想知道如何定義一個AMD
模塊。能夠先移步RequireJS官網。
JS有一條定律:Atwood's Law
any application that can be written in JavaScript, will eventually be written in JavaScript.
JS是能夠在服務端存在,因此出現了CommonJS
(A Module Format Optimized For The Server),使得JS不只僅在瀏覽器端應用,並且在服務端開始發光發熱。
CommonJS
是專一於
和AMD
不是一個服務層面。
a.js
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
複製代碼
上面代碼經過module.exports輸出變量x和函數addX。
b.js
var example = require('./a.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6
複製代碼
在ES6中,從語法層面就提供了模塊化的功能。然而受限於瀏覽器的實現程度,若是想要在瀏覽器中運行,仍是須要經過Babel等轉譯工具進行編譯。
person-module.js
var firstName = '北宸';
var lastName = '範';
export { firstName, lastName };
複製代碼
test-module.js
import {firstName,lastName} from './person-module.js';
console.log(`${lastName}${firstName}`)//範北宸
複製代碼
具體細節請參考Module的用法
模塊化方案 | 加載 | 同步/異步 | 瀏覽器 | 服務端 | 模塊定義 | 模塊引入 |
---|---|---|---|---|---|---|
Module Pattern | 取決於代碼 | 取決於代碼 | 支持 | 支持 | IIFE | 命名空間 |
AMD | 提早預加載 | 異步 | 支持 | 構建工具r.js | define | require |
Common | 值拷貝,運行時加載 | 同步 | 原生不支持 | 須要使用browserify提早打包編譯 | module.exports | require |
ES Modules(ES6) | 實時綁定,動態綁定,編譯時輸出 | 同步 | 支持 | 需用babel轉譯 | export | import |
本文參考連接: