多年来, 在 JavaScript 生态中出现了不同形式的模块化方案. 开发人员使用了明确定义的规范 (如 AMD 或 CommonJS) 以及简单的编码模式 (如通过揭示模块模式(revealing module pattern)) 来得到模块化解决方案的好处.
模块可以在浏览器的客户端使用, 也可以在 Node.JS 的服务器端使用. 有时也使用像 Babel 这样的工具将代码从一种模块格式转换为另一种格式. 所有这些都使得混乱的 JavaScript 模块状态变得更加混乱.
提示: 本文重点介绍 Node.JS 中的 ES 模块. 你可以通过查看 "CommonJS vs AMD vs RequireJS vs ES6 Modules" 来更好的比较模块系统.
一段简短的历史
先让我们来看看 ES 模块支持的关键里程碑:
2015 年 6 月: ES 模块在 ECMAScript 的 ES2015 规范中定义.
2015 年 6 月 - 2017 年 9 月: 主要浏览器为隐藏在开发者标志后面的 ES 模块添加实验性支持. 用 ES 模块开发 JavaScript 的主要方法是通过像 Babel 这样的工具来转换代码.
2017 年 9 月: Node.JS v8.5 https://nodejs.org/de/blog/release/v8.5.0/ 包含 ES 模块的实验性支持.
2017 年 9 月 - 2018 年 5 月: 主要浏览器开始支持 ES 模块规范, 没有开发者标志, 包括:
Chrome 61,2017 年 9 月 5 日
Safari 11,2017 年 9 月 18 日
Firefox 60,2018 年 5 月 8 日
2018 年 10 月: 创建了一个新的模块实施计划. 该计划包括几个阶段, 从一开始就遵循三个指导原则, 用新的实施方案取代当前的实验性实施方案:
遵守 ES 规范
Node 应尽可能的以和浏览器相同的方式执行操作
不破坏现有的 CommonJS 模块
提示: Node.JS 模块团队为新实现提供了更详细的指导原则 https://github.com/nodejs/modules/#features .
2019 年 10 月(暂定): 预计 Node 12 将会获得长期支持. 根据官方计划, 其目标是在此时发布对 ES 模块的完全支持.
为什么完整的 ES 模块支持里程碑对 Node.JS 如此重要?
有几个原因. 首先所有主流浏览器都支持 ES 模块 -- 你可以自己到这里去查看 https://caniuse.com/#feat=es6-module . 在 Node.JS 中支持服务器端的 ES 模块开箱即用, 将能够允许全栈开发人员非常自然地为客户端和服务器编写模块化, 可重用的 JavaScript 代码.
另外, Node.JS 中的实验性功能在未来版本中会受到非向后兼容的修改或删除. 话虽如此, 实验性的 ES 模块支持已经在 Node 中使用了好几年, 并且预计在 2019 年 10 月之前不会发生显着变化.
Node.JS 模块的当前状态
CommonJS 模块
目前(撰写本文时的 2019 年 7 月)Node.JS 中模块的事实标准是 CommonJS. CommonJS 模块在普通的 .JS 文件中用 module.exports 进行定义, 然后可以用 require() 函数在其他 .JS 文件中使用. 例如:
- // foo.jsmodule.exports = function() {
- return 'Hello foo!';
- }// index.jsvar foo = require('./foo');console.log(foo()); // Hello foo!
可以使用 node index.JS 命令运行此示例.
ES 模块
从 Node v8.5 开始, 开发人员已经能够使用 --experimental-modules 标志运行对 ES 模块规范的各种支持了. 从 Node v12.4 开始, 模块可以在 .mjs 文件中定义(或在某些情况下 https://nodejs.org/api/esm.html 在. JS 文件中). 例如:
- // foo.mjsexport function foo() {
- return 'Hello foo!';
- }// index.mjsimport { foo } from './foo.mjs';console.log(foo()); // Hello foo!
用 node --experimental-modules index.mjs 命令运行此示例.
在同一个应用中同时使用 CommonJS 和 ES 模块
在某些方面, 在浏览器中支持 ES 模块可能比在 Node 中更简单, 因为 Node 已经有了一个定义良好的 CommonJS 模块系统. 幸运的是, 开发人员可以同时使用这两种模块, 甚至从一种模块导入到另一种模块. 社区在在这方面做得非常出色.
假设我们有两个模块. 第一个是 CommonJS 模块, 第二个是 ES 模块(注意不同的文件扩展名):
- // cjs-module-a.jsmodule.exports = function() {
- return 'I am CJS module A';
- };// esm-module-a.mjsexport function esmModuleA() {
- return 'I am ESM Module A';
- };export default esmModuleA;
在 ES 模块脚本中使用 CommonJS 模块(请注意 .mjs 扩展名和使用 import 关键字):
// index.mjsimport esmModuleA from './esm-module-a.mjs';import cjsModuleA from './cjs-module-a.js';console.log(`esmModuleA loaded from an ES Module: ${esmModuleA()}`);console.log(`cjsModuleA loaded from an ES Module: ${cjsModuleA()}`);
用 node --experimental-modules index.mjs 命令运行此示例.
在标准的 CommonJS 脚本中使用 ES 模块 (注意 .JS 扩展名和使用 require() 函数):
- // index.JS// synchronously load CommonJS moduleconst cjsModuleA = require('./cjs-module-a');console.log(`cjsModuleA loaded synchronously from an CJS Module: ${cjsModuleA()}`);// asynchronously load ES module using CommonJSasync function main() {
- const {
- esmModuleA
- } = await import('./esm-module-a.mjs'); console.log(`esmModuleA loaded asynchronously from a CJS module: ${esmModuleA()}`);
- }
- main();
这些例子提供了如何在同一个程序中同时使用 CommonJS 和 ES 模块的基本演示. 你可以查看 Gil Tayar 在 "NodeJS 中的原生 ES 模块: 状态和未来方向, 第一部分" 中深入探讨的 CommonJS 和 ES 模块的互操作性.
Node.JS 模块的未来状态
在撰写本文时, 新模块的实施计划正处于第三和最后阶段. 计划在 Node 12 LTS 发布的同时完成阶段 3, 并且在没有 -experimental-modules 标志的情况下可以使用 ES 模块支持.
第 3 阶段可能会带来一些重大改进, 以完善 ES 模块的实现.
加载方案
开发人员希望模块加载系统具有灵活性和全功能. 以下是 Node.JS 模块加载器解决方案中的一些关键功能:
代码覆盖 / 检测: 使开发人员工具能够检索有关 CJS 和 ESM 模块使用情况的数据.
可插入加载器: 允许开发人员在他们的包中包含加载程序插件, 这些插件可以定义从特定文件扩展名或 mimetypes 加载模块的新行为, 甚至是没有扩展名的文件.
运行时加载器: 允许在运行时转换 import 语句中引用的文件.
- { "name": "@myorg/mypackage", "version": "1.0.0", "type": "module", "main": "./dist/index.js", "exports": { ".": "./src/mypackage.mjs", "./data": "./data/somedir/someotherdir/index.mjs"
- }
- }
来源: http://www.css88.com/web/node-js/14926.html