基於JS模塊化現狀談談選擇ES6模塊的原因


基於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);

相信大家對這段代碼都不陌生,它只是把變量和方法都封裝在本身作用域內的一種普通模式。其存在的缺點就是沒有幫我們處理依賴。

  • requirejsAMD (異步模塊依賴) : 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();

nodejs

感覺這種方式更酷吧,不僅可以定義變量的作用域,還可以定義模塊之間的依賴。可惜這是專爲 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 的內容。

tree-shacking

所以?瞭解代碼的靜態分析是使用什麼是不是也很重要呢?

答案是肯定的!

因爲有了這個控制,開發者工具可以檢測到一些 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


發佈了51 篇原創文章 · 獲贊 33 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章