跳到主要內容
黯羽輕揚每天積累一點點

module_ES6 筆記 13

免費2016-10-29#JS#es6模块语法#js模块化#es6模块与AMD#es6模块与CommonJS#es6模块机制

靜態模塊機制

寫在前面

這是 ES6 筆記的最後一篇內容,也是唯一一個將來才能使用的特性

將來是什麼時候?

或許是 HTTP2 普及的時候。但更大的可能是將來也「不能用」(還是只能在構建工具中用,僅存在於「編譯期」)

一。AMD,CMD 與 CommonJS

AMD/CMD,一點擴展知識如下:

CommonJS 是一套理論規範(比如 js 的理論規範是 ES),而 SeaJS, RequireJS 都是對 CommonJS 的 Modules 部分的具體實現

CommonJS 是面向瀏覽器外 (server 端) 的 js 制定的,所以是同步模塊加載,SeaJS 是 CommonJS 的一個實現,而 RequireJS 雖然也是對 CommonJS 的一個實現,但它是異步模塊加載,算是更貼近瀏覽器的單線程環境

總結:CommonJS 的 Modules 部分提出了模塊化代碼管理的理論,為了讓 js 可以模塊化加載,而 RequireJS, SeaJS 等各種實現可以稱為模塊化腳本加載器

CMD:Common 模塊定義,例如 SeaJS

AMD:異步模塊定義,例如 RequireJS

都是用來定義代碼模塊的一套規範,便於模塊化加載腳本,提高響應速度

CMD 與 AMD 的區別:

CMD 依賴就近。便於使用,在模塊內部可以隨用隨取,不需要提前聲明依賴項,所以性能方面存在些許降低(需要遍歷整個模塊尋找依賴項目)

AMD 依賴前置。必須嚴格聲明依賴項,對於邏輯內部的依賴項(軟依賴),以異步加載,回調處理的方式解決

(引自 [JS 編程常識](/articles/js 編程常識/#articleHeader9))

如果關注過 JS 模塊化,應該清楚這三者混亂的關係,ES6 模塊希望通過標準來結束這種混亂

二。ES6 模塊語法

1. 模塊作用域

module 引入了模塊作用域,特點如下:

  • 目前(2016/1/31 2016/10/29)沒有瀏覽器支持ES6 模塊(可能這樣的模塊加載機制不適合瀏覽器環境),利用 webpack 等工具可以把 import 的所有內容整合到一個文件中

  • ES6 模塊預設嚴格模式,無論加不加 'use strict';

  • 支持引入/導出時重命名,import/export {api as newApi},引入時重命名主要解決命名衝突,導出時重命名可以實現別名($jQuery

  • 支持預設引入/導出,能夠引入 CommonJS 和 AMD 模塊

  • 只可以在模塊的最外層作用域使用 import/export,且不能再條件語句中使用

總結:推進嚴格模式;兼容 CommonJS 和 AMD;只是單純的靜態模塊機制,沒有解決按需加載之類的問題

引入/導出時重命名,示例如下:

// 引入時重命名,解決命名衝突
import {flip as flipOmelet} from "eggs.js";
import {flip as flipHouse} from "real-estate.js";

// 導出時重命名,實現別名
function v1() { ... }
function v2() { ... }
export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

2.import

import {api1, api2...} from 'xxx.js' 語法,特點如下:

  • 支持 api 部分引入(不引入不需要的功能接口,當然,xxx.js 是完整加載的,部分引入只是作用域控制)

  • 如果 xxx.js 還有 import 語句,會深度優先加載執行

  • 已執行模塊會被忽略,避免形成循環引用

  • 支持預設引入,用來支持引入 CommonJS 和 AMD 包(default 就是 export 對象),import api from 'xxx.js' 等價於 import {default as api} from 'xxx.js'

  • 支持引入模塊對象,import * as apis from 'xxx.js'* 表示 xxx.jsexport 的所有東西,把 xxx.js 中導出的所有東西整合到 apis 對象中,通過 apis.xx 訪問

總結:加載機制類似於 CSS 的 @import,處理循環依賴的方式也類似;同樣兼容 CommonJS 和 AMD

作用域層面支持部分引入,有用,但意義不大,配合構建工具編譯時「剪枝」(tree shaking)更好一些

3.export

export {api1, api2...} 語法,特點如下:

  • 不需要在首行聲明,可以在模塊內外層作用域任何位置export

  • 可以聲明多個 export,但要保證 api 名稱無重複,名稱重複可能會出錯

  • 支持預設導出,export default api 或者 export {api as default}

  • 支持聚合導出,export {api1, api2...} from 'xxx.js' 等價於 import + export,但 export from 不會在當前模塊作用域引入各個 api 變量(引入後直接導出,無法引用

  • export 導出的 api 列表必須是字面量形式,不能遍歷數組導出數組元素

總結:加載時整理 export 列表,所以可以在外層任何位置 export;支持聚合,從各個第三方模塊抽出一部分整合起來;靜態限制,不允許動態導出

示例如下:

// 預設導出
 let myObject = {
  field1: value1,
  field2: value2
};
export {myObject as default};
// 等價於
export default {
  field1: value1,
  field2: value2
};

// 聚合導出
// 導入 "sri-lanka" 並將它導出的內容的一部分重新導出
export {Tea, Cinnamon} from "sri-lanka";
// 導入 "equatorial-guinea" 並將它導出的內容的一部分重新導出
export {Coffee, Cocoa} from "equatorial-guinea";
// 導入 "singapore" 並將它導出的內容全部導出
export * from "singapore";

三。模塊執行機制

ES6 標準沒有寫明具體模塊加載機制,交由最終實現來定,但明確規定了模塊執行機制,分為 4 個步驟

  1. 語法解析

檢查語法錯誤

  1. 加載

遞歸加載所有被 import 的東西,具體怎麼加載,沒有寫明,完全交由最終實現來定

  1. 連接

創建模塊作用域,並把所有被 import 進來的東西塞進作用域

如果 import 出錯,就會觸發錯誤,具體行為未知(因為還沒有瀏覽器已經走過了第 2 步)

  1. 運行時

執行每一個模塊的所有語句,此時遇到 import/export 就忽略掉,因為模塊相關的處理已經結束了

靜態限制

  • 只能在模塊最外層作用域使用 import/export,不能在條件語句中使用,也不能在函數作用域用

  • export 的標識符必須是字面量形式(要在源碼中有對應的聲明),不能遍歷數組再導出一堆東西

  • 模塊對象被凍結了,不能通過 hack 模塊對象來添加 polyfill 風格的新特性

  • 模塊的所有依賴必須在模塊代碼執行前加載、解析並連接完畢,不存在一種通過 import 來按需懶加載的語法

  • import 模塊產生的錯誤沒有對應的恢復機制。如果有一個模塊無法加載或連接,所有的模塊都不會執行,而且無法捕獲 import 錯誤

  • 無法在模塊加載依賴前執行其它代碼,這意味著無法控制模塊的依賴加載過程

因為存在這些限制,所以可能在 HTTP2 普及後,ES6 模塊機制還是不能在瀏覽器興起,像 CSS 的 @import 一樣,能用,但都不願意用

四。HTTP2 與模塊化

在 HTTP1.1 的環境下,為了減少 HTTP 請求數量,所有模塊化方案最終都依賴構建工具整合出單一文件

但 HTTP2 帶來了一些變革,也許能夠改變這種工程化流程,比如多路複用流(Multiplexed stream)和服務器端推送:

Http2 連接可以承載數十或數百個流的複用,多路複用意味著來自很多流的數據包能夠混合在一起通過同樣連接傳輸,兩列不同火車被混合在一起傳輸,當到達終點時,它們又被拆開組成兩列不同的火車。

客戶端請求一個資源 X,服務器端判斷也許客戶端還需要資源 Z,在無需事先詢問客戶端情況下將資源 Z 推送到客戶端,客戶端接受到後,可以緩存起來以備後用。

(引自 Http 2.0 協議簡介

多路複用流抹平了文件合併的優勢,服務端推送有助於解決深度 import 問題,所以 ES6 模塊可能會在瀏覽器環境興起

HTTP2 對於模塊化進程有重要意義,為在生產環境保持模塊化提供了機會,JS、CSS 甚至其它資源都可能迎來真正的模塊化

P.S. 關於 HTTP2 的更多細節,請查看 https://github.com/bagder/http2-explained

五。ES6 模塊現狀

As the various milestones of the roadmap are completed, browsers will be able to implement them. See the following trackers for the current status of the main browsers:

IE/Edge: Under Consideration

Firefox: In progress

Chrome: In progress

Webkit: Meta Bug

(引自 https://github.com/whatwg/loader

關於 ES6 模塊加載器的更多信息請關注這個 repo,ES6 規範沒有說明加載的具體實現,所以瀏覽器都卡在了加載器的實現上

參考資料

  • 《ES6 in Depth》:InfoQ 中文站提供的免費電子書

評論

暫無評論,快來發表你的看法吧

提交評論