ES6 中的 `export const` 与 `export default`
`export const` vs. `export default` in ES6
除了能够通过 export default
导入之外,我正在尝试确定这两者之间是否存在重大差异:
import myItem from 'myItem';
使用export const
我可以做到:
import { myItem } from 'myItem';
除此之外,and/or 用例还有其他区别吗?
Named exports are useful to export several values. During the import, one will be able to use the same name to refer to the corresponding value.
Concerning the default export, there is only a single default export per module. A default export can be a function, a class, an object or anything else. This value is to be considered as the "main" exported value since it will be the simplest to import.
这是命名导出与默认导出。 export const
是导出一个或多个 const 声明的命名导出。
强调:这里重要的是 export
keyword as const
用于声明一个或多个 const 声明。 export
也可以应用于其他声明,例如 class 或函数声明。
默认导出 (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
备注
- 默认导出的语法更简洁一些,因为它们的用例更常见 (See the discussion here)。
默认导出实际上是名称为 default
的命名导出,因此您可以使用命名导入来导入它:
import { default as MyDefaultExport } from "./MyFileWithADefaultExport";
小提示:请注意,当您从默认导出导入时,命名是完全独立的。这实际上对重构有影响。
假设您有一个像这样的 class 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'
现在,如果您将 Foo
class 重构为 Bar
并重命名文件,大多数 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'
当你设置默认值时,它被称为默认导出。每个文件只能有一个默认导出,您可以将其导入到另一个文件中,名称任意。当你不设置默认值时,它被称为命名导出,你必须将它导入另一个使用相同名称并在其中包含大括号的文件。
export default
在导入导出的 "thing" 时影响语法,当允许导入时,无论导出什么,通过选择 import
本身的名称,无论是什么导出时的名称,只是因为它被标记为 "default".
我喜欢(和使用)的一个有用的用例是允许导出一个 匿名 函数而无需 显式 命名它,并且只有在导入该函数时,才必须为其命名:
Example:
导出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
当使用 {}
语法导入函数(或变量)时,这意味着导入的任何内容在导出时 已经 命名,因此必须导入它通过 完全 相同的名称,否则导入将不起作用。
错误的例子:
默认函数必须先导入
import {divide}, square from './module_1.js
divide_1
未在 module_1.js
中导出,因此不会导入任何内容
import {divide_1} from './module_1.js
square
未在 module_1.js
中导出,因为 {}
告诉引擎明确搜索 named 导出只有。
import {square} from './module_1.js
更重要的区别是:export default
导出值,而export const
/export var
/export let
导出引用(或被称为活绑定)。在 nodejs 中尝试以下代码(使用版本 13 或更高版本默认启用 es 模块):
// 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);
Please pay attention to var y = x
statement, which is the default
.
webpack has similar build output. When large scale of modules are added to build, concatenateing text is not sustainable, and bundlers will use Object.defineProperty
to achieve binding(or called harmony exports in webpack). Please find detail in below code:
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);
/***/ })
]]);
Please find the difference behavior between /* harmony export (binding) */
and /* harmony default export */
.
ES 模块原生实现
es-modules-a-cartoon-deep-dive Mozilla 讲述了 es 模块的原因、内容和方式。
除了能够通过 export default
导入之外,我正在尝试确定这两者之间是否存在重大差异:
import myItem from 'myItem';
使用export const
我可以做到:
import { myItem } from 'myItem';
除此之外,and/or 用例还有其他区别吗?
Named exports are useful to export several values. During the import, one will be able to use the same name to refer to the corresponding value.
Concerning the default export, there is only a single default export per module. A default export can be a function, a class, an object or anything else. This value is to be considered as the "main" exported value since it will be the simplest to import.
这是命名导出与默认导出。 export const
是导出一个或多个 const 声明的命名导出。
强调:这里重要的是 export
keyword as const
用于声明一个或多个 const 声明。 export
也可以应用于其他声明,例如 class 或函数声明。
默认导出 (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
备注
- 默认导出的语法更简洁一些,因为它们的用例更常见 (See the discussion here)。
默认导出实际上是名称为
default
的命名导出,因此您可以使用命名导入来导入它:import { default as MyDefaultExport } from "./MyFileWithADefaultExport";
小提示:请注意,当您从默认导出导入时,命名是完全独立的。这实际上对重构有影响。
假设您有一个像这样的 class 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'
现在,如果您将 Foo
class 重构为 Bar
并重命名文件,大多数 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'
当你设置默认值时,它被称为默认导出。每个文件只能有一个默认导出,您可以将其导入到另一个文件中,名称任意。当你不设置默认值时,它被称为命名导出,你必须将它导入另一个使用相同名称并在其中包含大括号的文件。
export default
在导入导出的 "thing" 时影响语法,当允许导入时,无论导出什么,通过选择 import
本身的名称,无论是什么导出时的名称,只是因为它被标记为 "default".
我喜欢(和使用)的一个有用的用例是允许导出一个 匿名 函数而无需 显式 命名它,并且只有在导入该函数时,才必须为其命名:
Example:
导出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
当使用 {}
语法导入函数(或变量)时,这意味着导入的任何内容在导出时 已经 命名,因此必须导入它通过 完全 相同的名称,否则导入将不起作用。
错误的例子:
默认函数必须先导入
import {divide}, square from './module_1.js
divide_1
未在module_1.js
中导出,因此不会导入任何内容import {divide_1} from './module_1.js
square
未在module_1.js
中导出,因为{}
告诉引擎明确搜索 named 导出只有。import {square} from './module_1.js
更重要的区别是:export default
导出值,而export const
/export var
/export let
导出引用(或被称为活绑定)。在 nodejs 中尝试以下代码(使用版本 13 或更高版本默认启用 es 模块):
// 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);
Please pay attention to
var y = x
statement, which is thedefault
.
webpack has similar build output. When large scale of modules are added to build, concatenateing text is not sustainable, and bundlers will use
Object.defineProperty
to achieve binding(or called harmony exports in webpack). Please find detail in below code:
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);
/***/ })
]]);
Please find the difference behavior between
/* harmony export (binding) */
and/* harmony default export */
.
ES 模块原生实现
es-modules-a-cartoon-deep-dive Mozilla 讲述了 es 模块的原因、内容和方式。