For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/stage/2.7/proposal-export-star-default.md.
  • 简体中文
  • export all from S2.7

    中文标题:从模块全部导出

    提案概览
    提案速览

    该提案针对使用 export * from 'module' 时具名导出与默认导出之间的不对称性,因为当前规范排除了默认导出。它还旨在简化需要暴露默认导出的索引模块和动态模块包装。提案移除 export * 对 default 的特殊处理,使 default 能通过 export * 作为具名导出可用,并将现有歧义解决机制应用于默认导出。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    ECMAScript 提案:在 export * from 'module' 中包含默认导出

    阶段: 2.7

    作者:Nicolò Ribaudo(前作者:Guy Bedford)

    审阅者: Guy Bedford, Caio Lima

    规范: http://tc39.es/proposal-export-star-default/

    演示文稿:

    • 2016-11:为 Stage 1 进行了展示,但未达成共识(笔记)
    • 2026-05:为 Stage 1 进行了展示,并达成共识(幻灯片,笔记)
    • 2026-09:为 Stage 2 和 2.7 进行了展示,并达成共识(幻灯片)

    动机

    在当前规范中,export * from 'module' 会导出所有具名导出,但默认导出除外。

    不过,在一些用例中,能够在内部私有模块中定义 default 导出,并通过 export * 将其暴露出来是很有用的。

    1. 与具名导出的对称性

    考虑一个模块 X,它既有具名导出,又有默认导出,并通过另一个模块重新导出。然后我们在 reexport1.js 中用显式名称重新导出该模块,并在 reexport2.js 中用 export * 重新导出:

    x.js

    export default 'default';
    export let name = 'name';

    重新导出的变体:

    reexport1.js

    export { name, default } from './x.js';

    reexport2.js

    export * from './x.js';

    在第一个变体中,我们可以像它被显式导出那样导入默认导出:

    import { name } from './reexport1.js';
    import { default } from './reexport1.js';
    
    import * as A from './reexport1.js';
    A.default;
    // -> 'default'

    而在 export * 重新导出的变体中,默认导出不可用:

    import { name } from './reexport2.js';
    import { default } from './reexport2.js';
    // Syntax Error: 'default' is not exported
    
    import * as B from './reexport2.js';
    B.default
    // -> undefined

    这打破了用户的直觉,即 default 像其他任何具名导出一样是一个具名导出。

    2. 创建索引模块

    在 NodeJS 中,惯例是使用 index.js 作为包的主入口点。

    如果我们想使用 export * 语句创建这个模块,以暴露来自子文件夹的模块,我们就无法通过这种方式导出默认导出:

    index.js

    export * from './lib/package-core.js';
    export let extraInfo = 'abc';

    如果 lib/package-core.js 包含默认导出,我们就不会将其暴露出来。

    我们需要用以下方式显式导出默认导出:

    index.js

    export * from './lib/package-core.js';
    export { default } from './lib/package-core.js';
    export let extraInfo = 'abc';

    以确保我们获得预期的入口点模块值。

    这里的问题在于,如果我们从 lib/package-core.js 中移除默认导出,那么就会在 index.js 中得到一个 SyntaxError,提示默认导出不再存在。移除默认导出的更改需要分别在两个地方进行。

    上述模式在 npm 上的 index.js 模块中也已经广泛出现,形式如下:

    module.exports = require('./lib/package.js');

    此时我们知道 index.js 模块将与内部模块完全一致。

    3. 动态包装

    重新导出默认导出的情况也可以扩展到这样的用例:我们想要动态生成一个模块包装器,暴露另一个模块的导出,而无需事先知道其导出名称。

    考虑一个用例,比如 npm CDN,它允许像 https://npmcdn.com/lodash 这样的快捷 URL,然后在一个带完整版本号的 URL 如 https://npmcdn.com/lodash@4.17.2/index.js 上暴露模块。

    为 https://npmcdn.com/lodash 动态生成的模块可能如下所示:

    export * from './lodash@4.17.2/index.js';

    但如果存在默认导出,我们就必须提供不同的响应:

    export * from './lodash@4.17.2/index.js';
    export { default } from './lodash@4.17.2/index.js';

    如果我们在需要时没有包含默认导出,我们可能会缺少 default,但如果在不需要时包含了它,我们就会得到错误。因此,这一限制给原本简单的服务器响应增加了一个新的静态分析问题。

    提案

    该提案旨在从当前规范中移除 export * 对 default 的特殊处理,以简化 export * 的对称性,并支持上文所述的更轻松的模块包装用例。

    这一更改与现有规范向后兼容,并使得通过 export * 导入默认导出时不再产生语法错误,同时在这种情况下让 default 作为模块命名空间对象上的具名导出可用。

    http://guybedford.github.io/proposal-export-star-default/

    处理歧义

    在 export * 中包含默认导出的一个顾虑是如何处理冲突,但 export * 已经被设计为能够很好地处理歧义——当模块中的两个 export * 语句解析到相同的导出名称时,会抛出 SyntaxError,如 15.2.1.16.4 12.d.ii) 中所述:

    x.js

    export default 'x';
    export let name = 'nameX';

    y.js

    export default 'y';
    export let name = 'nameY';

    z.js

    export * from './x.js';
    export * from './y.ys';

    当我们尝试通过以下方式导入 name 时

    import { name } from './z.js';

    我们会得到一个 SyntaxError,提示 name 有歧义。

    可以通过显式指明从哪个模块导出 name 来解决这一冲突:

    z.js

    export * from './x.js';
    export * from './y.ys';
    export { name } from './x.js';

    现在当我们从 z.js 导入 name 时,它将得到正确解析。

    在这一规范更改下,同样的歧义解决过程将适用于默认导出:

    import { default } from './z.js';

    会抛出 SyntaxError。

    z.js

    export * from './x.js';
    export * from './y.ys';
    export { default } from './x.js';

    随后会通过提供显式优先级来解决该 SyntaxError,从而在处理通过 export * 产生的具名导出与默认导出之间的冲突时提供对称性。