为什么react能直接在esm中被具名导入?

疑问

react虽然同时提供了commonjs导出和esm导出,但是其esm导出的文件仍然是commonjs的,在并未经过任何转译的情况下,为什么能直接使用import具名导入它呢?在index.mjs中具名导入本地的commonjs模块会出错,我仿照react做了一个模块也是报错

复现

  1. 仅安装react

    npm i react
  2. index.mjs

    import { version } from "react";
    console.log(version)
  3. 执行

    node index.mjs

结果

成功打印react版本

其他信息

  1. react的package.json

    {
      "main": "index.js",
      "exports": {
         ".": {
           "react-server": "./react.shared-subset.js",
           "default": "./index.js"
         },
         "./package.json": "./package.json",
         "./jsx-runtime": "./jsx-runtime.js",
         "./jsx-dev-runtime": "./jsx-dev-runtime.js"
      }
    }
  2. react的index.js

    'use strict';
    
    if (process.env.NODE_ENV === 'production') {
      module.exports = require('./cjs/react.production.min.js');
    } else {
      module.exports = require('./cjs/react.development.js');
    }

    `

阅读 2.5k
2 个回答

破案了,node现在原生支持在esm中import一个cjs模块,详见https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#intero...。测试了一下,好像只支持模块,不支持本地导入。

There is the ECMAScript module loader:
....
It can be used to load JavaScript CommonJS modules. such modules are passed through the cjs-module-lexer to try to identify named exports, which are available if they can be determined through static analysis . Imported CommonJS modules have their URLs converted to absolute paths and are then loaded via the CommonJS module loader.
译:ECMAScript模块加载器可以用来加载CommonJS模块,通过cjs-module-lexer来尝试识别模块的命名导出,如果通过静态分析确定是可用的,将其 URL 转换为绝对路径,然后通过CommonJS模块加载器加载。
新手上路,请多包涵

问了下 ChatGPT,然后去看了 react/package.json 确实有这个 babel 插件

ChatGPT 回答的原文:

原因在于,虽然React的ESM导出文件是以CommonJS语法编写的,但React在打包时使用了一个工具——@babel/plugin-transform-modules-commonjs来将其转译成了符合ESM规范的代码。@babel/plugin-transform-modules-commonjs插件会将CommonJS的导出语法转译成ESM的导出语法,该语法可以被ESM识别和导入。

因此,使用import具名导入React的ESM导出文件是在执行了转译后的代码。而如果你自己编写的模块没有经过转译,那么就会出现类似于“unexpected token”的错误,因为ESM无法识别或导入CommonJS的模块。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏