在 Node.js 中使用原生的 ES 模块

Posted 前端外刊评论

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在 Node.js 中使用原生的 ES 模块相关的知识,希望对你有一定的参考价值。

本文译自:Using ES modules natively in Node.js,原作者是 Dr. Axel Rauschmayer,圈内称之为"德国阮一峰"。

Node.js 从 8.5.0 开始原生支持 ES 模块,不过还需要通过命令行参数控制。这还得感谢 Bradley Farias 对这个新特性的贡献。

本文就给大家详细讲讲这个特性。

举个栗子

文件结构:

 
   
   
 
  1. esm-demo/

  2.    lib.mjs

  3.    main.mjs

lib.mjs

 
   
   
 
  1. exportfunctionadd(x, y) {

  2.    return x + y;

  3. }

main.mjs

 
   
   
 
  1. import {add} from'./lib.mjs';

  2. console.log('Result: '+add(2, 3));

运行示例:

 
   
   
 
  1. $ node --experimental-modules main.mjs

  2. Result: 5

注意注意!

ES 的模块名称:

  • 所有的模块名称都是 URL —— 这对 Node.js 来说引入了新的东西;

  • 文件:最好使用相对的带 .mjs 后缀的路径来引用 ES 模块,这样可以保证对浏览器的兼容。如果不包含后缀名,则模块搜索方式与 CJS 保持一致。如果同时找到了 .mjs 和 .js 的文件,则使用 .mjs 文件;

    • 例如: ../util/tools.mjs

  • 类库模块:直接使用模块名,省略扩展名是最佳方式。这能与现阶段类库模块发布的方式保持最好的兼容;

    • 例如: lodash

  • 如何让 node_modules 里的模块在浏览器里工作还有待研究(不使用 bundler 工具)。一种可选方案是引入类 RequireJS 的配置方式,将裸路径转换到真实路径上。就目前而言,裸路径的模块在浏览器是中是用不了的;

ES 模块的特性:

  • 无法动态引入模块;但是很快就会引入动态操作符 import() 来实现;

  • 像 __dirname 和 __filename 和这样的元变量将不再提供,有一个新的提案引入将会给 ES 模块提供类似的方式——Domenic Denicola 提出 import.meta 。

 
   
   
 
  1. console.log(import.meta.url);

与 CJS 模块的互操作性

  • ES 模块可以 import CJS 模块,但是引入的模块只包含一个 default export——即 module.exports的值。已经开始计划让 CJS 模块输出具名 export(通过在文件开始加编译指令的方式),不过可能实现需要花一些时间。如果你想帮忙,看这里。


    1. import fs1 from'fs';

    2. console.log(Object.keys(fs1).length); // 86

    3. import * as fs2 from'fs';

    4. console.log(Object.keys(fs2)); // ['default']

  • 在 ES 模块中不能调用 require(),主要是因为:


    • 路径解析在两种规范中是很不一样的:ESM 不支持 NODE_PATH 和 require.extensions;它的模块标识符永远都是 URL,这会造成小小的区别;

    • 为了与浏览器保持最大的兼容性,ES 模块总是异步加载的。这种异步加载的方式与 CJS 通过 require() 同步加载的方式无法很好的在一起工作;

    • 同步模块的禁用同样也给在 ES 模块中使用顶级 await关上了大门(一个目前正在考虑的特性);

  • CJS 无法 require() ES 模块。原因与上一条提到的类似,同步加载异步的模块各种问题啊。不过也许可以通过 import() 来加载 ES 模块。


在老版本的 Node.js 上使用 ES 模块

好吧,如果你想在老的 Node.js 的使用 ES 模块,可以看看 John-David Dalton 的 @std/esm

小贴士:只要不使用 unlockables 特性,就可以保证与原生的 ES Module 完全兼容。

FAQ

什么时候可以不借助命令配置就可以使用 ES 模块?

目前的计划是,在 Node.js 10 LTS 版本中可以直接支持 ES 模块;

.mjs 在浏览器中可以工作么?

可以的,只需要提供正确的 Media Type( text/javascript 或者 application/javascript)即可。 .mjs 标准化和周边工具的升级正在进行中。

文件扩展名 .mjs 是 ES 模块所必须的么?

是的,对于 Node.js 来说是这样的。浏览器不在乎文件扩展名,只关心 Media Type。

为什么 Node.js 需要 .mjs

Node.js 本可以检查一个文件是 CJS 模块还是 ES 模块。在选择添加扩展名这个方案之前也讨论过多个其他方案。可以阅读本博的另外一篇了解。每种方案都有其优势和缺点。在我看来, .mjs 是正确的选择。


以上是关于在 Node.js 中使用原生的 ES 模块的主要内容,如果未能解决你的问题,请参考以下文章

node.js的模块化与包

使用node.js中fs模块的copyFileSync方法复制文件报错“operation not permitted, copyfile ‘G: est.txt‘ -> ‘G:Trash‘“(代码片

本地 ES 模块(浏览器或 Node.js)的热模块替换*没有* Webpack?没有构建工具

Node.js 全球化 es6 模块以像 ImportScripts 一样工作

Node.js入门 03:模块化规范 CommonJS 与 ES Module

如何欺骗 Node.js 将 .js 文件加载为 ES6 模块?