前言
ES6标準釋出後,module成為标準,标準使用是以export指令導出接口,以import引入子產品,但是在我們一貫的node子產品中,我們依然采用的是CommonJS規範,使用require引入子產品,使用module.exports導出接口。
一個子產品就是一個獨立的檔案。該檔案内部的所有變量,外部無法擷取。如果你希望外部能夠讀取子產品内部的某個變量,就必須使用
export
關鍵字輸出該變量。
export導出子產品
export文法聲明用于導出函數、對象、指定檔案(或子產品)的原始值。
// 導出單個特性
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName(){...}
export class ClassName {...}
// 導出清單
export { name1, name2, …, nameN };
// 重命名導出
export { variable1 as name1, variable2 as name2, …, nameN };
// 預設導出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
// Aggregating modules
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;
export有兩種子產品導出方式:命名式導出(名稱導出)和預設導出(定義式導出),命名式導出每個子產品可以多個,而預設導出每個子產品僅一個。
命名式導出
子產品可以通過export字首關鍵詞聲明導出對象,導出對象可以是多個。這些導出對象用名稱進行區分,稱之為命名式導出。
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function add (x, y) {
return x + y;
}
//------ main.js ------
import { square, add } from 'lib';
console.log(square(10)); //100
console.log(add(2,4)); //6
這樣導入的變量名必須和導出的名稱一緻。以上對于每一個要導出的變量都加了export,我們也可以直接導出一個清單,例如上面的lib.js可以改寫成:
//------ lib.js ------
const sqrt = Math.sqrt;
function square(x) {
return x * x;
}
function add (x, y) {
return x + y;
}
export {sqrt, square, add}
export
清單可以在子產品檔案最外層作用域的每一處聲明,不一定非要把它放在子產品檔案的末尾。
也可以直接導入整個子產品,此時的main.js子產品将變成這樣。
//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(10)); //100
console.log(lib.add(2,4)); //6
子產品導出時,可以指定子產品的導出成員。導出成員可以認為是類中的公有對象,而非導出成員可以認為是類中的私有對象:
var name = 'IT筆錄';
var domain = 'http://itbilu.com';
export {name, domain}; // 相當于導出
{name:name,domain:domain}
子產品導出時,我們可以使用as關鍵字對導出成員進行重命名:
var name = 'IT筆錄';
var domain = 'http://itbilu.com';
export {name as siteName, domain};
注意:export 指令規定的是對外接口,必須與子產品内部變量建立一一對應的關系
// 寫法一
export var m = 1;
// 寫法二
var m = 1;
export {m};
// 寫法三
var n = 1;
export {n as m};
// 報錯
export 1;
// 報錯
var m = 1;
export m;
報錯的寫法原因是:沒有提供對外的接口,第一種直接輸出1,第二種雖然有變量m,但還是直接輸出1,導緻無法解構。
同樣的,
function
和
class
的輸出,也必須遵守這樣的寫法。
// 報錯
function f() {}
export f;
// 正确
export function f() {};
// 正确
function f() {}
export {f};
預設導出
預設導出也被稱做定義式導出。命名式導出可以導出多個值,但在在import引用時,也要使用相同的名稱來引用相應的值。而預設導出每個導出隻有一個單一值,這個輸出可以是一個函數、類或其它類型的值,這樣在子產品import導入時也會很容易引用。
export default function() {}; // 可以導出一個函數
export default class(){}; // 也可以出一個類
注意這裡預設導出不需要用{}。當然也可以使用混合的導出。
//------ lib.js ------
export default function() {...};
export function each() {...};
//------ main.js ------
import _,{ each } from 'lib';
export加default和不加的差別
// 第一組
export default function crc32() { // 輸出
// ...
}
import crc32 from 'crc32'; // 輸入
// 第二組
export function crc32() { // 輸出
// ...
};
import {crc32} from 'crc32'; // 輸入
第一組是使用export default時,對應的import語句不需要使用大括号;
第二組是不使用export default時,對應的import語句需要使用大括号。
export default指令用于指定子產品的預設輸出。顯然,一個子產品隻能有一個預設輸出,是以export default指令隻能使用一次。是以,import指令後面才不用加大括号,因為隻可能唯一對應export default指令。
export default就是輸出一個叫做default的變量或方法,然後系統允許你為它取任意名字
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 錯誤
export default var a = 1;
// 上面代碼中,export default a的含義是将變量a的值賦給變量default。是以,最後一種寫法會報錯。
// 注意,不能使用 var、let 或 const 用于導出預設值 export default。
因為export default 指令的本質是将後面的值,賦給default變量,是以可以直接将一個值寫在export default之後
// 正确
export default 42;
// 報錯 原因這一句報錯是因為沒有指定對外的接口,而前一句指定外對接口為default。
export 42;
如果想在一條import語句中,同時輸入預設方法和其他接口,可以寫成下面這樣
import _, { each, each as forEach } from 'lodash';
===============
export default function (obj) {
// ···
}
export function each(obj, iterator, context) {
// ···
}
export { each as forEach };//暴露出forEach接口,預設指向each接口,即forEach和each指向同一個方法。
export default也可以用來輸出類
// MyClass.js
export default class { ... }
// main.js
import MyClass from 'MyClass';
let o = new MyClass();
命名式導出與預設導出
預設導出可以了解為另一種形式的命名導出,預設導出可以認為是使用了default名稱的命名導出。
下面兩種導出方式是等價的:
const D = 123;
export default D;
export { D as default };
在導出多個值時,命名導出非常有用。在導入期間,必須使用相應對象的相同名稱。但是,可以使用任何名稱導入預設導出,例如:
let k; export default k = 12; // 在 test.js檔案中定義導出
import m from './test' // 由于k 是預設導出,是以可以自由使用import m 替代import k
console.log(m); // 輸出為 12
Import加載子產品
export定義了子產品的對外接口後,其他JS檔案就可以通過import來加載這個子產品,
// main.js
import {firstName, lastName, year} from './profile';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
import指令接受一對大括号,裡面指定要從其他子產品導入的變量名,必須與被導入子產品(
profile.js
)對外接口的名稱相同。
如果想重新給導入的變量一個名字,可以用as關鍵字,
import { lastName as surname } from './profile';
import後的from 可以指定需要導入子產品的路徑名,可以是絕對路徑,也可以是相對路徑, .js路徑可以省略,如果隻有子產品名,不帶有路徑,需要有配置檔案指定。
注意,
import
指令具有提升效果,會提升到整個子產品的頭部,首先執行。(是在編譯階段執行的)
// circle.js。輸出兩個函數
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
// main.js 加載在個子產品
import { area, circumference } from './circle';
console.log('圓面積:' + area(4));
console.log('圓周長:' + circumference(14));
//上面寫法是逐一指定要加載的方法,整體加載的寫法如下。
import * as circle from './circle';
console.log('圓面積:' + circle.area(4));
console.log('圓周長:' + circle.circumference(14));
import * as circle from './circle';
// 下面兩行都是不允許的
circle.foo = 'hello';
circle.area = function () {};
-
和import
指令隻能在子產品的頂層,不能在代碼塊之中。否則會文法報錯。export
- 這樣的設計,可以提高編譯器效率,但是沒有辦法實作運作時加載。
- 因為require是運作時加載,是以import指令沒有辦法代替require的動态加載功能。
- 是以引入了import()函數。完成動态加載。