全面解析ECMAScript 6模塊系統

快速使用Romanysoft LAB的技術實現 HTML 開發Mac OS App,並銷售到蘋果應用商店中。
 
HTML開發Mac OS App 視頻教程》
 
官方QQ羣:(申請加入,說是我推薦的
  • App實踐出真知 434558944       App實踐出真知
  • App學習交流 452180823          App實踐出真知
 

在任何一個大型應用中模塊化是很常見的。ES6的模塊爲JavaScript提供了這個特性,而且爲這些模塊提供了許多選擇方法來導出和引入對象。Ravi Kiran 在《 Modules in ECMAScript 6 (ES6) 》一文中主要討論了ES6模塊系統。如下爲該文章的簡譯內容:javascript

不管使用何種編程語言開發大型應用,最關鍵的特性就是代碼模塊化。這個概念在不一樣的編程語言裏有着不一樣的命名,在C裏爲頭部文件,C++和C#裏爲命名空間,Java中爲包,名稱不同但解決的是同一問題。正如《 ECMAScript 6 – New language improvements in JavaScript 》系列文章中第一篇所提到的那樣,最初JavaScript並非用來編寫大量代碼的,好比建立大型框架、App應用等。就在咱們由於JavaScript缺乏對模塊的支持而編寫大量代碼時,開源開發者提出了一些標準,如CommoneJs模塊模型、異步模塊定義(AMD)以及一些庫,來實現模塊化。在過去幾年裏,這些庫得到了普遍關注,併成功應用到多個企業規模級的應用程序中。php

ES6爲JavaScript帶來了模塊特性。瀏覽器實現這一特性還須要一段時間,由於它們必須定義一個方法來動態下載文件。在瀏覽器支持該特性之前,咱們可使用編譯器,如 Traceur、6to五、ES6 Module Loader以及其它可讓ES6模塊轉換成ES5的轉碼器。css

JavaScript模塊系統的現狀java

CommonJS模塊系統python

CommonJs是一個由開源開發者組成的團隊,主要圍繞JavaScript實現一些API及開展研發實踐。該團隊提出了一個JavaScript模塊規範。每一個文件均可看成一個模塊,而且每一個文件能夠訪問兩個對象:require和export。require用來接收字符串(模塊名),並返回該模塊輸出的對象。export對象用來導出該模塊的方法和變量。require方法返回的就是export對象。模塊同步加載。服務器端JavaScript引擎Node.js就是用的這個模塊系統。sql

異步模塊定義(AMD)npm

AMD是一個採用異步方式加載依賴模塊的模塊系統。若是模塊在不一樣文件中,它們將採用XHR進行加載。某一模塊將等其所依賴的模塊一一加載後纔會被執行。AMD模塊必須是一個函數,並做爲參數傳入define函數中。函數的返回值將傳輸給全部依賴的模塊,所得到返回值又將做爲參數傳給模塊方法。Require.js庫中實現了AMD。編程

TypeScript模塊瀏覽器

TypeScript,做爲JavaScript的超集,也提供了一個模塊系統。當它被編譯時,便開始使用JavaScript模塊模式。TypeScript模塊使用module關鍵字定義,任何被輸出的對象必須使用export關鍵字定義。import關鍵字用來將其它模塊加載入模塊中,並捕捉該模塊導出的對象。TypeScript模塊是同步加載的。服務器

ES6模塊系統

ES6模塊系統啓發於上述現有模塊系統,它具備如下特性:

  1. 使用export關鍵詞導出對象。這個關鍵字能夠無限次使用;
  2. 使用import關鍵字將其它模塊導入某一模塊中。它可用來導入任意數量的模塊;
  3. 支持模塊的異步加載;
  4. 爲加載模塊提供編程支持。

接下來讓咱們經過具體編程方法看看每個特性。

導出對象

在現有的模塊系統中,每一個JavaScript代碼文件在ES6中都是一個模塊。只有模塊中的對象須要被外部調用時,模塊纔會輸出對象,其他則都是模塊的私有對象。該處理方式將細節進行封裝,僅導出必要的功能。

從模塊裏導出對象,ES6爲咱們提供了不一樣方法,見下面的討論。

內聯導出

ES6模塊裏的對象可在建立它們的聲明中導出。一個模塊中可無數次使用export,全部的對象將被一塊兒導出。請看下面的例子:

export class Employee{ constructor(id, name, dob){ this.id = id; this.name=name; this.dob= dob; } getAge(){ return (new Date()).getYear() - this.dob.getYear(); } } export function getEmployee(id, name, dob){ return new Employee(id, name, dob); } var emp = new Employee(1, "Rina", new Date(1987, 1, 22));

案例中的模塊導出了兩個對象: Employee類,getEmployee函數。因對象emp未被導出,因此其仍爲模塊私有。

導出一組對象

儘管內聯導出頗有效,但在大規模模塊中,它就很難發揮做用了,由於咱們可能沒法追蹤到模塊導出來的對象。在這種狀況下,更好的辦法是,在模塊的末尾單獨進行導出聲明,以導出該模塊中的所有對象。

使用單獨導出聲明重寫上一案例中的模塊,結果以下:

class Employee{ constructor(id, name, dob){ this.id = id; this.name=name; this.dob= dob; } getAge(){ return (new Date()).getYear() - this.dob.getYear(); } } function getEmployee(id, name, dob){ return new Employee(id, name, dob); } var x = new Employee(1, "Rina", new Date(1987, 1, 22)); export {Employee, getEmployee};

在導出時,重命名對象也是能夠的。以下例所示,Employee在導出時名字改成了Associate,函數GetEmployee更名爲getAssociate。

export {
    Associate as Employee, getAssociate as getEmployee };

Default導出

使用關鍵字default,可將對象標註爲default對象導出。default關鍵字在每個模塊中只能使用一次。它既能夠用於內聯導出,也能夠用於一組對象導出聲明中。

下面案例展現了在組導出語句中使用default:

export default { Employee, getEmployee };

導入模塊

現有模塊可使用關鍵字import導入到其它模塊。一個模塊能夠被導入任意數量的模塊中。下文展現了導入模塊的不一樣方式。

無對象導入

若是模塊包含一些邏輯要執行,且不會導出任何對象,此類對象也能夠被導入到另外一模塊中。以下面案例所示:

import './module1.js';

導入默認對象

採用Default導出方式導出對象,該對象在import聲明中將直接被分配給某個引用,以下例中的「d」。

import d from './module1.js';

導入命名的對象

正如以上討論的,一個模塊能夠導出許多命名對象。若是另外一模塊想導入這些命名對象,須要在導入聲明中一一列出這些對象。舉個例子:

import {Employee, getEmployee} from './module1.js';

固然也可在同一個聲明中導入默認對象和命名對象。這種狀況下,默認對象必須定義一個別名,以下例。

import {default as d, Employee} from './module1.js';

導入全部對象

以上幾種狀況,只有import聲明中列舉的對象纔會被導入並被使用,而其它對象則沒法在導入模塊中使用。固然,這就要求用戶瞭解哪些對象能夠導出並加以利用。若是模塊導出大量對象,另外一模塊想引入全部導出的對象,就必須使用以下聲明:

import * as allFromModule1 from './module1.js';

allFromModule1這一別名將指向全部從module1導出的對象。在導入模塊中,它們做爲屬性可被訪問。

可編程式的按需導入

若是想基於某些條件或等某個事件發生後再加載須要的模塊,可經過使用加載模塊的可編程API(programmatic API)來實現。使用System.import方法,可按程序設定加載模塊。這是一個異步的方法,並返回Promise。

該方法的語法示例以下:

System.import('./module1.js') .then(function(module1){ //use module1 }, function(e){ //handle error });

若是模塊加載成功且將導出的模塊成功傳遞給回調函數,Promise將會經過。若是模塊名稱有誤或因爲網絡延遲等緣由致使模塊加載失敗,Promise將會失敗。

ES6模塊使用現狀

到目前爲止,全部瀏覽器還不能天然支持ES6模塊,因此在瀏覽器加載以前,咱們須要使用轉譯器(transpiler)將代碼轉換成ES5。直到如今,我一直使用Traceur做爲個人轉譯器,建議你們使用相同的工具將模塊代碼轉化爲瀏覽器可識別的代碼。讓咱們看看編譯ES6模塊的幾種不一樣的方法。

使用Traceur動態編譯ES6模塊

當瀏覽器加載腳本後,咱們可使用Traceur的客戶端庫動態編譯ES6模塊。使用該方法,運行模塊無需運行任何命令。咱們要作得就是,在頁面上加載Traceur庫,及添加代碼腳原本運行WebPageTranscoder。

<script> new traceur.WebPageTranscoder(document.location.href).run(); </script>

如今,咱們就能夠在script標籤內,將類型指定成模塊,以此導入任何一個ES6文件。

<script type="module"> import './modules/import1.js'; </script>

類型指定爲模塊的任何腳本標籤將被ES6客戶端庫獲取並處理。上面代碼塊中的導入語句將發送AJAX請求,捕獲相應的JavaScript文件,並載入它。若是模塊內部引用了另外一個模塊,單獨的AJAX請求將發出,以加載與引用模塊相對應的文件。

使用Traceur命令編譯ES6模塊

使用Traceur命令能夠將ES6模塊編譯成AMD或者CommonJS模塊。這個方法有兩大優勢。

  1. 模塊完成編譯,瀏覽器沒必要執行額外動做;
  2. 若是應用已經使用ES5及AMD(或CommonJs)模塊系統構建了一半,程序的另外一半也可使用ES6,並被編譯爲這些模塊系統中的任何一個,而不是當即把整個應用編譯成ES6。

爲了使用編譯完成的AMD/CommonJs的模塊,咱們須要包含支持模塊系統的庫。我我的比較傾向AMD,因此我將在這裏介紹一下它。CommonJS模塊的步驟和這個差很少。

下面這句命令是用來讓包含ES6模塊的文件夾編譯成AMD,並把它們存儲在一個單獨的文件夾:

traceur --dir modules es5Modules --modules=amd

使用CommonJs,你須要在上面命令中使用commonjs代替modules。

在這裏,modules指的是包含ES6的文件夾,es5Modules指的是輸出目錄。若是查看es5Modules文件夾下的任何文件,你將看到該AMD定義塊。require.js支持AMD,因此咱們能夠在HTML頁面中,使用script引入require.js,並用data-main屬性指明開始文件,就像下面這樣:

<script src="bower_components/requirejs/require.js" data-main="es5Modules/import2.js"></script>

使用Traceur Grunt Task編譯ES6模塊

使用命令編譯模塊很累並且更容易出錯。咱們可使用grunt-traceur自動化編譯過程。此時,你須要安裝NPM包。

npm intall grunt-traceur –save

Grunt任務所需數據與提供給命令的數據同樣。下面是任務的一些配置:

traceur: { options: { modules: "amd" }, custom: { files: [{ expand: true, cwd: 'modules', src: ['*.js'], dest: 'es5Modules' }] } }

如今你能夠在控制檯裏使用下面的命令來運行上面的Grunt任務:

grunt traceur

正如你所看見的那樣,它和咱們使用命令所產生的效果是同樣的。

結論

任何一個大型應用中,模塊化十分必要。ES6模塊爲JavaScript提供了該特性,這些模塊提供了衆多選擇來導出和引入對象。我很期待該特性被瀏覽器支持的那一天,到時咱們無需加載任何第三方庫便可建立、加載JavaScript模塊。目前流行的客戶端MV*框架Angular.js在其2.0版本(目前還在開發中)中就使用了ES6的模塊化。

讓咱們開始使用模塊系統,從而讓咱們的代碼更具組織和可讀性。

相關文章
相關標籤/搜索