ES6 中的“export const”与“export default”

IT技术 javascript ecmascript-6 es6-modules
2021-01-27 21:40:22

我试图确定这两者之间是否有任何重大差异,除了能够通过以下方式导入export default

import myItem from 'myItem';

使用export const我可以做到:

import { myItem } from 'myItem';

除此之外还有什么区别和/或用例吗?

6个回答

这是命名导出与默认导出。export const是一个命名导出,它导出一个或多个 const 声明。

强调:这里重要的是用于声明一个或多个 const 声明export关键字constexport也可以应用于其他声明,例如类或函数声明。

默认导出 ( export default)

每个文件可以有一个默认导出。导入时,您必须指定一个名称并像这样导入:

import MyDefaultExport from "./MyFileWithADefaultExport";

你可以给它任何你喜欢的名字。

命名导出 ( export)

使用命名导出,每个文件可以有多个命名导出。然后导入您想要用大括号括起来的特定导出:

// ex. importing multiple exports:
import { MyClass, MyOtherClass } from "./MyClass";
// ex. giving a named import a different name by using "as":
import { MyClass2 as MyClass2Alias } from "./MyClass2";

// use MyClass, MyOtherClass, and MyClass2Alias here

或者可以在同一语句中使用默认值和命名导入:

import MyDefaultExport, { MyClass, MyOtherClass} from "./MyClass";

命名空间导入

也可以从对象的文件中导入所有内容:

import * as MyClasses from "./MyClass";
// use MyClasses.MyClass, MyClasses.MyOtherClass and MyClasses.default here

笔记

  • 语法倾向于默认导出稍微简洁一点,因为它们的用例更常见(请参阅此处的讨论)。
  • 默认导出实际上是具有名称的命名导出,default因此您可以使用命名导入来导入它:

    import { default as MyDefaultExport } from "./MyFileWithADefaultExport";
    

export default导入导出的“事物”时会影响语法,允许导入时,无论已导出的内容,通过在其import本身中选择名称,无论导出时的名称是什么,仅仅因为它被标记为“默认”。

我喜欢(并使用)的一个有用的用例是允许导出匿名函数而无需显式命名它,并且只有在导入该函数时,才必须为其命名:


例子:

导出2个函数,一个是default

export function divide( x ){
    return x / 2;
}

// only one 'default' function may be exported and the rest (above) must be named
export default function( x ){  // <---- declared as a default function
    return x * x;
}

导入以上函数。default一个人取一个名字

// The default function should be the first to import (and named whatever)
import square, {divide} from './module_1.js'; // I named the default "square" 

console.log( square(2), divide(2) ); // 4, 1

{}语法用于导入函数(或变量),这意味着,无论是进口的是已经在出口时命名的,因此必须由导入它确切的名称相同,否则进口是行不通的。


错误示例:

  1. 默认函数必须先导入

    import {divide}, square from './module_1.js
    
  2. divide_1没有被导出module_1.js,因此什么都不会被导入

    import {divide_1} from './module_1.js
    
  3. square未在 中导出module_1.js,因为{}告诉引擎显式搜索命名导出。

    import {square} from './module_1.js
    
我还在这里学习了新的英语单词:“错误”+1
2021-03-20 21:40:22
这并不意味着它输出单一的东西。您可以在同一个module中有多个命名和一个默认值。默认只是意味着 - 如果您在导入时未指定名称,则它是默认导出,即import something from代替import { somethingNamed } from.
2021-03-21 21:40:22

小注意:请注意,当您从默认导出导入时,命名是完全独立的。这实际上对重构有影响。

假设您有这样一个Foo带有相应导入的类:

export default class Foo { }

// The name 'Foo' could be anything, since it's just an
// Identifier for the default export
import Foo from './Foo'

现在,如果你重构你的FooBar并重命名文件,大多数 IDE 不会触及你的导入。所以你最终会得到这个:

export default class Bar { }

// The name 'Foo' could be anything, since it's just an
// Identifier for the default export.
import Foo from './Bar'

尤其是在 TypeScript 中,我非常欣赏命名导出和更可靠的重构。区别只是缺少default关键字和花括号。顺便说一句,这还可以防止您在导入中输入拼写错误,因为您现在可以进行类型检查。

export class Foo { }

//'Foo' needs to be the class name. The import will be refactored
//in case of a rename!
import { Foo } from './Foo'
可以用 an 重命名它as实际上不是该源注释中的重点。感谢您的反对;p
2021-03-16 21:40:22
'Foo' 必须是类名。 ” - 不!您可以像使用默认导出一样轻松地进行import { Foo as Anything } from …操作import Anything from …
2021-03-25 21:40:22
我没有反对,但是我不确定这个论点是否有说服力。我不知道在重构单个module时我是否希望我的 IDE 重命名所有导入,这正是module化的意义:-) 而且它似乎更像是一个 IDE“问题”,而不是选择导出样式的理由…
2021-03-28 21:40:22
我同意我在这里使用命名导出是为了开发人员的用户体验——但是,你可能会争辩说 Typescript 本身就是关于这一点的。我经常重构,并且考虑到我通常每个文件有一个类(在我的情况下:React Component),我绝对希望导入遵循重命名的组件,而不是创建断开连接。当然,这可能有意义,也可能没有意义,具体取决于个人开发人员。
2021-04-03 21:40:22
我发现一篇文章说同样的事情。也许一个合理的位置可能是:我们应该export default用于导出项目的主要对象,特别是从 npm 包(它替换 a module.exports =)。但是,在项目内部,最好仅使用命名导出。
2021-04-04 21:40:22

更重要的区别是:export default出口值,而export const/ export var/export let出口引用(被称为活的结合)。在 nodejs 中尝试以下代码(使用 13 或更高版本默认启用 es module):

// a.mjs

export let x = 5;
// or
// let x = 5;
// export { x }

setInterval(() => {
  x++;
}, 1000);

export default x;
// index.mjs
import y, { x } from './1.mjs';

setInterval(() => {
  console.log(y, x);
}, 1000);
# install node 13 or above
node ./index.mjs

我们应该得到以下输出:

6 5
7 5
8 5
...
...

为什么我们需要这种差异

最有可能的是,export default用于 commonjs 的兼容性module.exports

如何使用 bundler(rollup, webpack) 实现这一点

对于上面的代码,我们使用 rollup 来捆绑。

rollup ./index.mjs --dir build 

和构建输出:

// build/index.js

let x = 5;
// or
// let x = 5;
// export { x }

setInterval(() => {
  x++;
}, 1000);

var y = x;

setInterval(() => {
  console.log(y, x);
}, 1000);

请注意var y = x声明,即default.

webpack 有类似的构建输出。当大量的module被加入构建时,拼接文本是不可持续的,打包器会使用它Object.defineProperty来实现绑定(或者在 webpack 中称为和谐导出)。请在以下代码中找到详细信息:

main.js
...
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/        }
/******/    };
...
// 1.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
/* 0 */,
/* 1 */
/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "x", function() { return x; });
let x = 5;
// or
// let x = 5;
// export { x }

setInterval(() => {
  x++;
}, 1000);

/* harmony default export */ __webpack_exports__["default"] = (x);


/***/ })
]]);

请找出/* harmony export (binding) */之间的不同行为/* harmony default export */

ES module原生实现

Mozilla 的es-modules-a-cartoon-deep-dive讲述了 es module的原因、内容和方式。

文档

命名导出可用于导出多个值。在导入过程中,可以使用相同的名称来引用相应的值。

关于默认导出,每个module只有一个默认导出。默认导出可以是函数、类、对象或其他任何东西。该值将被视为“主要”导出值,因为它是最容易导入的。