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

  • Post author:
  • Post category:其他


本文译自: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


    是正确的选择。







版权声明:本文为JXH600ZRQR原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。