深入了解 Export 和 import
深入了解 export 和 import
Export和import指令有幾種語法變體。
在上一篇文章中,我們看到了一個簡單的用法,現(xiàn)在讓我們探討更多的示例。
export 之前申明
我們可以將任何聲明(無論是變量、函數(shù)還是類)置于export之前,從而將其標(biāo)記為導(dǎo)出。
例如,這里所有的導(dǎo)出都是有效的:
// export an array
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// export a constant
export const MODULES_BECAME_STANDARD_YEAR = 2015;
// export a class
export class User {
constructor(name) {
this.name = name;
}
}
請注意,在類或函數(shù)之前導(dǎo)出并不會使其成為函數(shù)表達式。它仍然是一個函數(shù)聲明,盡管已經(jīng)導(dǎo)出。
大多數(shù)JavaScript風(fēng)格指南不建議在函數(shù)和類聲明后使用分號。
這就是為什么在導(dǎo)出類和導(dǎo)出函數(shù)的結(jié)尾不需要分號的原因:
export function sayHi(user) {
alert(`Hello, ${user}!`);
} // no ; at the end
除聲明外的 export
此外,我們可以把出口分開。
這里我們首先聲明,然后導(dǎo)出:
// ?? say.js
function sayHi(user) {
alert(`Hello, ${user}!`);
}
function sayBye(user) {
alert(`Bye, ${user}!`);
}
export {sayHi, sayBye}; // a list of exported variables
Import *
通常,我們把要導(dǎo)入的內(nèi)容放在花括號import{…},像這樣:
// ?? main.js
import {sayHi, sayBye} from './say.js';
sayHi('John'); // Hello, John!
sayBye('John'); // Bye, John!
但如果有很多東西需要導(dǎo)入,我們可以使用import * as 將所有東西作為對象導(dǎo)入,例如:
// ?? main.js
import * as say from './say.js';
say.sayHi('John');
say.sayBye('John');
乍一看,“導(dǎo)入所有內(nèi)容”似乎是一件很酷的事情,簡單說來,為什么我們要明確地列出我們需要導(dǎo)入的內(nèi)容呢?
嗯,有幾個原因。
現(xiàn)代構(gòu)建工具(webpack和其他工具)將模塊捆綁在一起,并對它們進行優(yōu)化,以加速加載和刪除未使用的內(nèi)容。
比如說,我們在我們的項目中添加了一個帶有許多函數(shù)的第三方庫say.js:
// ?? say.js
export function sayHi() { ... }
export function sayBye() { ... }
export function becomeSilent() { ... }
現(xiàn)在,如果我們在項目中只使用say.js中的一個函數(shù):
// ?? main.js
import {sayHi} from './say.js';
然后優(yōu)化器將看到這一點,并從捆綁代碼中刪除其他函數(shù),從而使構(gòu)建更小。這被稱為“tree-shaking”。
顯式列出要導(dǎo)入的內(nèi)容會給出更短的名稱:sayHi()而不是say.sayHi()。
顯式的導(dǎo)入列表可以更好地概述代碼結(jié)構(gòu):使用了什么和在哪里使用。它使代碼支持和重構(gòu)更容易。
Import “as”
我們還可以使用as在不同的名稱下導(dǎo)入。
例如,為了簡潔起見,讓我們將sayHi導(dǎo)入到本地變量hi中,并將sayBye作為bye導(dǎo)入:
// ?? main.js
import {sayHi as hi, sayBye as bye} from './say.js';
hi('John'); // Hello, John!
bye('John'); // Bye, John!
Export “as”
導(dǎo)出也有類似的語法。
讓我們導(dǎo)出hi和bye函數(shù):
// ?? say.js
...
export {sayHi as hi, sayBye as bye};
hi和bye是對外來者的正式稱呼,用于進口產(chǎn)品:
// ?? main.js
import * as say from './say.js';
say.hi('John'); // Hello, John!
say.bye('John'); // Bye, John!
Export default
在實踐中,主要有兩種模塊。
包含庫和函數(shù)包的模塊,如上面的say.js。
聲明單個實體的模塊,例如,模塊User. js只導(dǎo)出類User。
大多數(shù)情況下,第二種方法是首選的,這樣每個“東西”都駐留在自己的模塊中。
當(dāng)然,這需要很多文件,因為所有東西都需要自己的模塊,但這根本不是問題。實際上,如果文件的名稱很好,并且被結(jié)構(gòu)化到文件夾中,代碼導(dǎo)航就會變得更容易。
模塊提供了一個特殊的export default (" the default export ")語法,使"每個模塊一件事"的方式看起來更好。
將export default放在要導(dǎo)出的實體之前:
// ?? user.js
export default class User { // just add "default"
constructor(name) {
this.name = name;
}
}
每個文件可能只有一個默認的導(dǎo)出。
然后不帶大括號導(dǎo)入:
// ?? main.js
import User from './user.js'; // not {User}, just User
new User('John');
不帶大括號的導(dǎo)入看起來更好。開始使用模塊時的一個常見錯誤是完全忘記花括號。記住,import對命名的導(dǎo)出需要花括號而默認的導(dǎo)出不需要花括號。
Named export Default export
export class User {...} export default class User {...}
import {User} from ... import User from ...
從技術(shù)上講,我們可能在一個模塊中同時有默認導(dǎo)出和命名導(dǎo)出,但在實踐中,人們通常不會將它們混合在一起。模塊有命名的exports或默認的exports。
由于每個文件最多可能有一個默認導(dǎo)出,因此導(dǎo)出的實體可能沒有名稱。
例如,這些都是完全有效的默認導(dǎo)出:
export default class { // no class name
constructor() { ... }
}
export default function(user) { // no function name
alert(`Hello, ${user}!`);
}
// export a single value, without making a variable
export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
export class { // Error! (non-default export needs a name)
constructor() {}
}
The “default” name
在某些情況下,default關(guān)鍵字用于引用默認的導(dǎo)出。
例如,將一個函數(shù)與它的定義分開導(dǎo)出:
function sayHi(user) {
alert(`Hello, ${user}!`);
}
// same as if we added "export default" before the function
export {sayHi as default};
或者,另一種情況,讓我們說一個模塊user.js導(dǎo)出了一個主要的“默認”東西,以及一些命名的東西(很少有這種情況,但它發(fā)生了):
// ?? user.js
export default class User {
constructor(name) {
this.name = name;
}
}
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
下面是如何導(dǎo)入一個指定的默認導(dǎo)出:
// ?? main.js
import {default as User, sayHi} from './user.js';
new User('John');
最后,如果將所有東西*作為對象導(dǎo)入,那么默認屬性就是默認的export:
// ?? main.js
import * as user from './user.js';
let User = user.default; // the default export
new User('John');
A word against default exports
命名導(dǎo)出是顯式的。它們精確地命名了它們導(dǎo)入的內(nèi)容,所以我們從它們那里得到了這些信息;這是件好事。
命名導(dǎo)出強制我們在導(dǎo)入時使用正確的名稱:
import {User} from './user.js';
// import {MyUser} won't work, the name must be {User}
而對于默認導(dǎo)出,我們總是在導(dǎo)入時選擇名稱:
import User from './user.js'; // works
import MyUser from './user.js'; // works too
// could be import Anything... and it'll still work
因此,團隊成員可能會使用不同的名稱來導(dǎo)入相同的東西,這不是很好。
通常,為了避免這種情況并保持代碼的一致性,有一個規(guī)則,即導(dǎo)入的變量應(yīng)該對應(yīng)于文件名,
例如:
import User from './user.js';
import LoginForm from './loginForm.js';
import func from '/path/to/func.js';
...
Re-export
" Re-export "語法從…允許導(dǎo)入并立即導(dǎo)出(可能是另一個名稱),像這樣:
export {sayHi} from './say.js'; // re-export sayHi
export {default as User} from './user.js'; // re-export default
為什么需要這樣做?讓我們來看一個實際的用例。
想象一下,我們正在編寫一個“包”:一個包含大量模塊的文件夾,其中一些功能被導(dǎo)出到外部(像NPM這樣的工具允許我們發(fā)布和分發(fā)這些包,但我們不必使用它們),而許多模塊只是“助手”,供其他包模塊內(nèi)部使用。
文件結(jié)構(gòu)可能是這樣的:
auth/
index.js
user.js
helpers.js
tests/
login.js
providers/
github.js
facebook.js
...
我們希望通過單個入口點公開包的功能。
換句話說,想要使用我們的包的人,應(yīng)該只從“主文件”auth/index.js導(dǎo)入。
是這樣的:
import {login, logout} from 'auth/index.js'
“主文件”auth/index.js導(dǎo)出了我們想在包中提供的所有功能。
其思想是,外部的人,也就是使用我們包的其他程序員,不應(yīng)該干涉它的內(nèi)部結(jié)構(gòu),搜索我們包文件夾中的文件。我們只導(dǎo)出auth/index.js中必要的部分,其余部分則不被窺探。
由于實際導(dǎo)出的功能分散在包中,我們可以將其導(dǎo)入auth/index.js并從中導(dǎo)出:
// ?? auth/index.js
// import login/logout and immediately export them
import {login, logout} from './helpers.js';
export {login, logout};
// import default as User and export it
import User from './user.js';
export {User};
...
現(xiàn)在我們包的用戶可以從“auth/index.js”中導(dǎo)入{login}。
語法export…從…只是這種進出口的縮寫:
// ?? auth/index.js
// re-export login/logout
export {login, logout} from './helpers.js';
// re-export the default export as User
export {default as User} from './user.js';
...
出口的顯著差異是……與導(dǎo)入/導(dǎo)出相比,重新導(dǎo)出的模塊在當(dāng)前文件中不可用。所以在上面的auth/index.js示例中,我們不能使用重新導(dǎo)出的登錄/注銷函數(shù)。
Re-exporting the default export
155/5000 重新導(dǎo)出時,默認導(dǎo)出需要單獨處理。
假設(shè)我們有User .js和導(dǎo)出默認類User,并想重新導(dǎo)出它:
// ?? user.js
export default class User {
// ...
}
export * from './user.js'; // to re-export named exports
export {default} from './user.js'; // to re-export the default export
這種重新導(dǎo)出默認導(dǎo)出的奇怪現(xiàn)象是一些開發(fā)人員不喜歡默認導(dǎo)出而更喜歡命名導(dǎo)出的原因之一
