推荐阅读:https://zh.javascript.info/import-export
export导出
我们可以通过在声明之前放置 export 来标记任意声明为导出,无论声明的是变量,函数还是类都可以。
例如,这里的所有导出均有效:
// 导出数组
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// 导出 const 声明的变量
export const MODULES_BECAME_STANDARD_YEAR = 2015;
// 导出类
export class User {
constructor(name) {
this.name = name;
}
}
导出与声明分开
另外,我们还可以将 export 分开放置。
下面的例子中,我们先声明函数,然后再导出它们:
function sayHi(user) {
alert(`Hello, ${user}!`);
}
function sayBye(user) {
alert(`Bye, ${user}!`);
}
export {sayHi, sayBye}; // 导出变量列表
Import *
通常,我们把要导入的东西列在花括号 import {...} 中,就像这样:
import {sayHi, sayBye} from './say.js';
sayHi('John'); // Hello, John!
sayBye('John'); // Bye, John!
如果有很多要导入的内容,我们可以使用 import * as 将所有内容导入为一个对象
import * as say from './say.js';
say.sayHi('John');
say.sayBye('John');
Import “as” 和 Export “as”
我们也可以使用 as 让导入具有不同的名字。
import {sayHi as hi, sayBye as bye} from './say.js';
hi('John'); // Hello, John!
bye('John'); // Bye, John!
export {sayHi as hi, sayBye as bye};
现在 hi 和 bye 是在外面使用时的正式名称
Export default 导出的是一个模块
在实际中,主要有两种模块。
包含库或函数包的模块,像上面的 say.js。
声明单个实体的模块,例如模块 user.js 仅导出 class User。
大部分情况下,开发者倾向于使用第二种方式,以便每个“东西”都存在于它自己的模块中。
当然,这需要大量文件,因为每个东西都需要自己的模块,但这根本不是问题。实际上,如果文件具有良好的命名,并且文件夹结构得当,那么代码导航(navigation)会变得更容易。
模块提供了一个特殊的默认导出 export default 语法,以使“一个模块只做一件事”的方式看起来更好。
将 export default 放在要导出的实体前:
export default class User { // 只需要添加 "default" 即可
constructor(name) {
this.name = name;
}
}
每个文件应该只有一个 export default:
……然后将其导入而不需要花括号:
import User from './user.js'; // 不需要花括号 {User},只需要写成 User 即可
new User('John');
不用花括号的导入看起来很酷。刚开始使用模块时,一个常见的错误就是忘记写花括号。所以,请记住,import 命名的导出时需要花括号,而 import 默认的导出时不需要花括号。
命名导出
由于每个文件最多只能有一个默认的导出,因此导出的实体可能没有名称。
例如,下面这些都是完全有效的默认的导出:
export default class { // 没有类名
constructor() { ... }
}
export default function(user) { // 没有函数名
alert(`Hello, ${user}!`);
}
// 导出单个值,而不使用变量
export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
不指定名称是可以的,因为每个文件只有一个 export default,因此不带花括号的 import 知道要导入的内容是什么。
如果没有 default,这样的导出将会出错:
export class { // Error!(非默认的导出需要名称)
constructor() {}
}
“default” 名称
在某些情况下,default 关键词被用于引用默认的导出。
例如,要将函数与其定义分开导出:
function sayHi(user) {
alert(`Hello, ${user}!`);
}
// 就像我们在函数之前添加了 "export default" 一样
export {sayHi as default};
或者,另一种情况,假设模块 user.js 导出了一个主要的默认的导出和一些命名的导出(这种情况很少见,但确实会发生):
export default class User {
constructor(name) {
this.name = name;
}
}
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
这是导入默认的导出以及命名的导出的方法:
import {default as User, sayHi} from './user.js';
new User('John');
如果我们将所有东西 * 作为一个对象导入,那么 default 属性正是默认的导出:
import * as user from './user.js';
let User = user.default; // 默认的导出
new User('John');
命名的导出会强制我们使用正确的名称进行导入:
import {User} from './user.js';
// 导入 {MyUser} 不起作用,导入名字必须为 {User}
……对于默认的导出,我们总是在导入时选择名称:
import User from './user.js'; // 有效
import MyUser from './user.js'; // 也有效
// 使用任何名称导入都没有问题
重新导出
“重新导出(Re-export)”语法 export ... from ... 允许导入内容,并立即将其导出(可能是用的是其他的名字),就像这样:
export {sayHi} from './say.js'; // 重新导出 sayHi
export {default as User} from './user.js'; // 重新导出 default
重新导出默认导出
重新导出时,默认导出需要单独处理。
假设我们有一个 user.js 脚本,其中写了 export default class User,并且我们想重新导出类 User:
export default class User {
// ...
}
我们可能会遇到两个问题:
- export User from './user.js' 无效。这会导致一个语法错误。
要重新导出默认导出,我们必须明确写出 export {default as User},就像上面的例子中那样。
- export * from './user.js' 重新导出只导出了命名的导出,但是忽略了默认的导出。
如果我们想将命名的导出和默认的导出都重新导出,那么需要两条语句:
export * from './user.js'; // 重新导出命名的导出
export {default} from './user.js'; // 重新导出默认的导出
动态导入
下面这样的 import 行不通:
import ... from getModuleName(); // Error, only from "string" is allowed
if(...) {
import ...; // Error, not allowed!
}
{
import ...; // Error, we can't put import in any block
}
import() 表达式
let modulePath = prompt("Which module to load?");
import(modulePath)
.then(obj => <module object>)
.catch(err => <loading error, e.g. if no such module>)
//或者导入 say 中的两个函数
let {hi, bye} = await import('./say.js');
hi();
bye();
//或者 默认的导出
let obj = await import('./say.js');
let say = obj.default;
// or, in one line: let {default: say} = await import('./say.js');
say();
尽管 import() 看起来像一个函数调用,但它只是一种特殊语法,只是恰好使用了括号(类似于 super())。
因此,我们不能将 import 复制到一个变量中,或者对其使用 call/apply。因为它不是一个函数。