基於JS模塊化現狀談談選擇ES6模塊的原因
本文轉載自:衆成翻譯
譯者:旭日雲中竹
鏈接:http://www.zcfy.cc/article/1010
原文:http://blog.js-republic.com/start-of-art-of-javascript-modularization-why-choose-es2015/
這篇文章,我們將瞭解爲什麼JS社區有必要選擇ES6模塊。
現狀
要明白這種重要性,首先我們需要描述一下JS的實際情況。過去5年,JavaScript 發展得非常迅猛,大多數開發人員幾乎沒意識到當前已經有 5
種方式,可以爲 JavaScript 腳本和應用創建模塊了!
- 原始的 IIFE () : 這是最古老,也是比較簡單的創建 JS 模塊的方法了。以下是用
IIFE
實現的簡單模塊化示例:
const myModule = (function (...deps){
// JavaScript chunk
return {hello : () => console.log(‘hello from myModule’)};
})(dependencies);
相信大家對這段代碼都不陌生,它只是把變量和方法都封裝在本身作用域內的一種普通模式。其存在的缺點就是沒有幫我們處理依賴。
- AMD (異步模塊依賴) : Require.js 很受歡迎,它可以給模塊注入依賴,還允許動態地加載 JS 塊。
<pre>define(‘myModule’, [‘dep1’, ‘dep2’], function (dep1, dep2){
// JavaScript chunk, with a potential deferred loading
return {hello: () => console.log(‘hello from myModule’)};
});
// anywhere else
require([‘myModule’], function (myModule) {
myModule.hello() // display ‘hello form myModule’
});
效率高,可惜有點冗長,而且不能在 Node.js 本地運行。
- CommonJs : Node.js 平臺的默認格式. 通常用法類似這樣:
// file1.js
modules.export = {
hello : () => console.log(‘hello from myModule’)
}
// file2;
const myModule = require('./file1.js');
myModule.hello();
感覺這種方式更酷吧,不僅可以定義變量的作用域,還可以定義模塊之間的依賴。可惜這是專爲 Node
設計的,不支持在瀏覽器運行,也不能異步加載模塊。但是,它可以在前端app使用,藉助 Browserify
或者其他工具來轉換,就可以讓它在瀏覽器運行了。
- UMD (通用模塊依賴) : 到這裏,我們發現還沒有一種既可以同時兼容瀏覽器和 Node 服務器的解決方案。AMD 適合瀏覽器,CommonJS 適合 Node.
UMD 就來嘗試解決這個問題了。它通過把 AMD 和 CommonJS 結合起來,使之在各種需求下都是可集成的,代碼大概如下:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
typeof define === 'function' && define.amd ? define(factory) :
(factory());
}(this, function () {
// JavaScript chunk
return {
hello : () => console.log(‘hello from myModule’)
}
});
該模式根據內容來判斷選擇 AMD 還是 CommonJS. 這種模式對於打包 多環境
的庫,如 lodash
或者 moment.js
是十分適合的。
因此,爲何要加一個新模塊類型 ?
首先,這些解決方案沒有一個是由TC39團隊定義的標準。ECMA6 現在已經被大量的開發者所使用,那爲何不選擇該版本JS定義的標準(ES6模塊)呢?
這個標準提供了一個更加靈活有力的解決方式。我推薦你看這篇文章, 瞭解 ES6模塊的全部屬性,因爲這篇文章,我把重點放在了ES6模塊這個性質上。它能夠更加精確地定義模塊間哪些需要被 exposed/imported
。一起看看下面的代碼:
// file1.js
const f1 = ()=> console.log(‘f1’);
const f2 = ()=> console.log(‘f2’);
const f3 = ()=> console.log(‘f3’);
export {f1, f2, f3};
// file2.js
import {f1, f2} from “./file1”;
f1(); // display ‘f1’
f2(); // display ‘f2’
從這裏可以看出,只需要靜態地聲明我們想要 import
的內容就可以。與其它模式不同,像 CommonJS 我們可以動態加載需要的文件。如果我們在 CommonJS 裏面使用這個例子,就會有點不同:
// file1.js
const f1 = ()=> console.log(‘f1’);
const f2 = ()=> console.log(‘f2’);
const f3 = ()=> console.log(‘f3’);
modules.exports = {f1,f2,f3};
// file2.js
const file1 = require(‘./file1’);
file1.f1(); // display ‘f1’
file1.f2(); // display ‘f2’
file1[process.ENV.funcName]();
很明顯,最後一行無法呈現成真正的代碼,但它表明了一些值超出了可控範圍,無法在靜態分析中被預知。這裏,我們實質上是可以調用 f3
,因爲用 CommonJs
(AMD、IIFE 或者 UMD) 我們都是無法限制 import
的內容。
所以?瞭解代碼的靜態分析是使用什麼是不是也很重要呢?
答案是肯定的!
因爲有了這個控制,開發者工具可以檢測到一些 bug。 如果你使用 WebPack 2 或者 Rollup.js 將會更有趣,結合 Tree Shaking
, 你編譯的文件將會更小。Tree Shaking
的功能就是把不用用到的代碼移除掉。
這就是 tree shaking
的一個例子 :
原文件
//-------------
// main.js
import {cube} from './maths.js';
console.log( cube( 5 ) ); // 125
//-------------
// maths.js
export function square ( x ) {
return x * x;
}
// This function gets included
export function cube ( x ) {
return x * x * x;
}
輸出文件
function cube ( x ) {
return x * x * x;
}
console.log( cube( 5 ) ); // 125
Mathieu Breton CTO chez JS-Republic