【问题标题】:`export const` vs. `export default` in ES6ES6 中的 `export const` 与 `export default`
【发布时间】:2016-02-10 06:10:49
【问题描述】:

我正在尝试确定这两者之间是否存在任何重大差异,除了能够通过以下操作使用export default 导入:

import myItem from 'myItem';

使用export const 我可以做到:

import { myItem } from 'myItem';

除此之外还有其他区别和/或用例吗?

【问题讨论】:

  • 使用const 将使标识符只读。因此,对于原始值,您可以认为它是不可变的。请注意,值本身不是不可变的,因此可以更改对象、数组等——只是不能重新分配。
  • @spmurrayzzz:FWIW,导入绑定也是不可变的,就像const
  • @FelixKling:至少从外面看。虽然它们可能不是恒定的,但可以更改导出。
  • @Bergi:对,这就是我说 import bindings 的原因;)
  • @Bergi:我们不是已经有一个涵盖默认导出和命名导出的问题了吗?找不到任何东西,但感觉这肯定已经被问过了。

标签: javascript ecmascript-6 es6-modules


【解决方案1】:

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

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

默认导出 (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";
    

【讨论】:

    【解决方案2】:

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

    一个我喜欢(并使用)的有用用例是允许导出一个匿名函数而无需显式命名它,并且仅当该函数是导入,必须给它一个名字:


    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
    

    {} 语法用于导入函数(或变量)时,这意味着导入的任何内容在导出时已经命名,因此必须通过exact 同名,否则无法导入。


    错误示例:

    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 中导出,因为 {} 告诉引擎仅显式搜索 named 导出。

      import {square} from './module_1.js
      

    【讨论】:

    • 这并不意味着它导出一个单一的东西。您可以在同一个模块中拥有多个命名和一个默认值。默认只是意味着 - 如果您在导入时未指定名称,则它是默认导出,即import something from 而不是import { somethingNamed } from
    • 我还在这里学到了新的英文单词:“错误”+1
    【解决方案3】:

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

    假设你有一个像这样的类 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 类重构为 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'
    

    【讨论】:

    • "'Foo' 必须是类名。" - 不!您可以像使用默认导出操作 import Anything from … 一样轻松地执行 import { Foo as Anything } from …
    • 可以as重命名它真的不是那个源评论的重点。感谢您的反对;p
    • 我没有投反对票,但我不确定这个论点是否令人信​​服。我不知道在重构单个模块时是否希望我的 IDE 重命名所有导入,这正是模块化的意义:-) 而且它似乎更像是一个 IDE“问题”而不是选择导出样式的理由…
    • 我同意我在这里使用命名导出是为了开发人员的用户体验——但是,你可能会争辩说 Typescript 本身就是这样。我经常重构,并且考虑到我通常每个文件都有一个类(在我的情况下:React 组件),我绝对希望导入遵循重命名的组件,而不是创建断开连接。当然,这可能有意义,也可能没有意义,具体取决于各个开发人员。
    • 我找到了an article,它说了同样的话。也许一个合理的位置可能是:我们应该使用export default 来导出项目的主要对象,特别是从npm 包中(它替换module.exports =)。但是,在项目内部,最好只使用命名导出。
    【解决方案4】:

    更重要的区别在于: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);
    
    

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

    webpack 有类似的构建输出。当添加大量模块构建时,拼接文本是不可持续的,bundlers 将使用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 模块原生实现

    es-modules-a-cartoon-deep-dive by Mozilla 讲述了 es 模块的原因、内容和方法。

    【讨论】:

      【解决方案5】:

      来自the documentation

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

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

      【讨论】:

        【解决方案6】:

        当你放默认时,它被称为默认导出。每个文件只能有一个默认导出,并且可以将其导入到另一个文件中,使用任何您想要的名称。当您不设置默认值时,它称为命名导出,您必须将其导入到另一个文件中,使用相同的名称并在其中包含花括号。

        【讨论】:

          【解决方案7】:

          我遇到了浏览器不使用ES6的问题。

          我已经解决了:

           <script type="module" src="index.js"></script>
          

          类型模块告诉浏览器使用 ES6。

          export const bla = [1,2,3];
          
          import {bla} from './example.js';
          

          那么它应该可以工作了。

          【讨论】:

            猜你喜欢
            • 2017-03-10
            • 2018-04-11
            • 2019-08-04
            • 2018-11-29
            • 1970-01-01
            • 2017-01-09
            • 1970-01-01
            相关资源
            最近更新 更多