到目前为止,我们已经看到了如何导入/导出(import/export)多个内容,也可以用“as”语法导入/导出为其他名称。
在开发中,模块包含:

  • 一个库,一组函数,就像 lib.js 这样。
  • 或者在 user.js 中描述了一个实体,比如 class User 这样,整个模块只有这个类。

大部分情况下,开发者倾向于使用第二种方式,因此每个“thing”都存在于自己的模块中。
当然,如果每个文件都需要自己的模块,这就使得文件很多,但是这不算什么大问题。实际上,如果文件命名以及文件夹结构得当,代码导航会变得更容易。
模块提供特殊的默认导出 export default 语法,以使得“一个模块只做一件事”看起来更好。
默认导出要求下列的 exportimport 语句:

  1. export default 放在模块“主导出(main export)”之前。
  2. import 导入时不使用花括号

例如,user.js 导出 class User

  1. // 📁 user.js
  2. export default class User { // 只要添加“default”即可
  3. constructor(name) {
  4. this.name = name;
  5. }
  6. }

…在 main.js 中添加导入:

  1. // 📁 main.js
  2. import User from './user.js'; // 不需要花括号 {User}, 仅仅是 User 就可以了
  3. new User('John');

不用花括号的导入看起来很酷。开始使用模块时常见的错误就是忘记花括号。所以请记住,命名导入需要使用花括号,而默认导入不需要。

命名导出 默认导出
export class User {...} export default class User {...}
import {User} from ... import User from ...

当然,每个文件只有一个“默认”导出。
我们可能在单个模块中同时使用默认导出和命名导出,但是在日常开发中,开发者一般不会这样做。模块要么是命名导出要么是默认导出。
另外需要注意的是命名导出必须(理应)具有名称,而 export default 可能是匿名的(没有名称)
例如,下面这些都是完全有效的默认导出:

  1. export default class { // 没有类名
  2. constructor() { ... }
  3. }
  4. export default function(user) { // 没有函数名
  5. alert(`Hello, ${user}!`);
  6. }
  7. // 导出一个值而不使用变量
  8. export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

这些都是可行的,因为每个文件只有一个 export default。相反,省略命名导入的名称将会出错:

  1. export class { // Error!(非命名导出需要名称)
  2. constructor() {}
  3. }

“Default” 别名

“default”关键词用于默认导出的别名,常用于我们需要引用单独导出和其他脚本的情况。
例如,如果我们已经声明了一个函数,然后导出它 export default(和定义分开):

  1. function sayHi(user) {
  2. alert(`Hello, ${user}!`);
  3. }
  4. export {sayHi as default}; // 和我们在函数前添加“export default”一样

又如,假设模块 user.js 导出一个默认导出“default”和几个命名导出(虽然很少出现,但是会发生):

  1. // 📁 user.js
  2. export default class User {
  3. constructor(name) {
  4. this.name = name;
  5. }
  6. }
  7. export function sayHi(user) {
  8. alert(`Hello, ${user}!`);
  9. }

那么,如何导入默认导出和命名导出:

  1. // 📁 main.js
  2. import {default as User, sayHi} from './user.js';
  3. new User('John');

再如,我们想要把 * 作为对象导入,那么 default 属性就是默认导出:

  1. // 📁 main.js
  2. import * as user from './user.js';
  3. let User = user.default;
  4. new User('John');

我应该使用默认导出吗?

开发者应该谨慎使用默认导出,因为这将会使代码更难维护。
命名导出是显式的。它们准确命名导入的内容,因此我们能得到更多的信息,这对于代码阅读与维护都是非常有利的。
此外,命名导出会强制我们使用正确的名称来导入:

  1. import {User} from './user.js';
  2. // 使用 {MyUser} 导入将不起作用,导入名字应该为 {User}

对于默认导出,我们总是在导入时选择名称:

  1. import User from './user.js'; // works
  2. import MyUser from './user.js'; // works too
  3. // 使用任何名称导入都没有问题

对于相同的导入,团队成员可能使用不同的命名,因此,默认导入的命名可能会被滥用,
通常,为了避免这种情况并保持代码的整洁一致,可以遵从这条规则,即导入的变量应该与文件名相对应,例如:

  1. import User from './user.js';
  2. import LoginForm from './loginForm.js';
  3. import func from '/path/to/func.js';
  4. ...

另一种解决方案是在任何地方都使用命名导出。即使只导出一个东西,也仍然使用命名导出,而不是默认导出 default