作者:BrianDeSousa出现了不同形式的模块化方案。开发人员使用定义明确的规范(例如AMD或CommonJS)和简单的编码模式(例如通过揭示模块模式)来获得模块化解决方案的好处。模块可以在浏览器的客户端使用,也可以在Node.js的服务器端使用。有时像Babel这样的工具被用来将代码从一种模块格式转换为另一种模块格式。所有这些都让凌乱的JavaScript模块状态变得更加混乱。提示:本文重点介绍Node.js中的ES模块。通过查看“CommonJSvsAMDvsRequireJSvsES6Modules”,您可以更好地比较模块系统。简要历史让我们看看ES模块支持的关键里程碑:2015年6月:ES模块在ECMAScript的ES2015规范中定义。2015年6月-2017年9月:主要浏览器添加了对隐藏在开发者徽标后面的ES模块的实验性支持。使用ES模块开发JavaScript的主要方式是通过Babel等工具来转译代码。2017年9月:Node.jsv8.5包括对ES模块的实验性支持。2017年9月-2018年5月:主流浏览器开始支持ESModules规范,没有开发者标志,包括:Chrome61,Safari11于2017年9月5日,Firefox60于2018年9月18日,2018年5月2018年10月8日:创建了一个新模块实施计划。该计划分为几个阶段,从一开始就遵循三个指导原则,用新的实现替换当前的实验性实现:遵守ES规范Node应该尽可能以与浏览器相同的方式执行操作,而不破坏现有的一些CommonJS模块提示:Node.js模块团队为新的实现提供了更详细的指南。2019年10月(暂定):预计对Node12的长期支持。根据官方计划,目标是在此时发布对ES模块的全面支持。为什么完整的ES模块支持里程碑对Node.js如此重要?有几个原因。首先,所有主流浏览器都支持ES模块——你可以在这里自己检查一下。在开箱即用的Node.js中支持服务器端ES模块将允许全栈开发人员非常自然地为客户端和服务器编写模块化、可重用的JavaScript代码。此外,Node.js中的实验性功能可能会在未来的版本中进行非向后兼容的修改或删除。话虽如此,实验性ES模块支持已经在Node中使用了好几年,预计在2019年10月之前不会发生重大变化。Node.js模块的当前状态CommonJS模块目前(撰写本文时为2019年7月)事实上的标准Node.js中的模块是CommonJS。CommonJS模块在带有module.exports的普通.js文件中定义,然后通过require()函数在其他.js文件中可用。例如://foo.jsmodule.exports=function(){return'Hellofoo!';}//index.jsvarfoo=require('./foo');控制台日志(foo());//你好富!此示例可以使用nodeindex.js命令运行。ES模块从Nodev8.5开始,开发人员已经能够使用--experimental-modules标志来运行对ES模块规范的各种支持。从Nodev12.4开始,模块可以在.mjs文件中定义(或者在某些情况下在.js文件中)。例如://foo.mjsexportfunctionfoo(){return'Hellofoo!';}//index.mjsimport{foo}来自'./foo.mjs';控制台日志(foo());//你好foo!使用node--experimental-modulesindex.mjs命令运行这个例子。在同一应用程序中使用CommonJS和ES模块在某些方面,在浏览器中支持ES模块可能比在Node中更简单,因为Node已经具有定义良好的CommonJS模块系统。幸运的是,开发人员可以同时使用这两个模块,甚至可以从一个模块导入另一个模块。社区在这方面做得非常出色。假设我们有两个模块。第一个是CommonJS模块,第二个是ES模块(注意不同的文件扩展名)://cjs-module-a.jsmodule.exports=function(){return'IamCJSmoduleA';};//esm-module-a.mjsexport函数esmModuleA(){return'IamESMModuleA';};exportdefaultesmModuleA;在ES模块脚本中使用CommonJS模块(注意.mjs扩展名和import关键字的使用)://index.mjsimportesmModuleAfrom'./esm-module-a.mjs';从'./cjs-module-a.js'导入cjsModuleA;console.log(`esmModuleA从ES模块加载:${esmModuleA()}`);console.log(`cjsModuleA从ES模块加载:${cjsModuleA()}`);使用node--experimental-modulesindex.mjs命令运行此示例。在标准CommonJS脚本中使用ES模块(注意.js扩展和require()函数的使用)://index.js//同步加载CommonJS模块constcjsModuleA=require('./cjs-module-a');安慰。log(`cjsModuleA从CJS模块同步加载:${cjsModuleA()}`);//使用CommonJSasyncfunctionmain(){const{esmModuleA}=awaitimport('./esm-module-a.mjs');console.log(`esmModuleA从CJS模块异步加载:${esmModuleA()}`);}main();这些示例提供了如何在同一程序中一起使用CommonJS和ES模块的基本演示。您可以在“NodeJS中的原生ES模块:状态和未来方向,第1部分”中查看GilTayar对CommonJS和ES模块互操作性的深入研究。Node.js模块的未来状态在撰写本文时,新模块的实施计划处于第三个也是最后一个阶段。第3阶段计划与Node12LTS版本大约同时完成,ES模块支持将在没有-experimental-modules标志的情况下提供。第3阶段可能会带来一些重大改进,以完善ES模块的实施。加载方案开发人员需要灵活性和全功能的模块加载系统。以下是Node.js模块加载器解决方案中的一些主要功能:代码覆盖率/检测:使开发人员工具能够检索有关CJS和ESM模块使用情况的数据。可插入加载器:允许开发人员在他们的包中包含加载器插件,这些插件定义了从特定文件扩展名或mime类型加载模块的新行为,甚至是没有扩展名的文件。运行时加载器:允许在运行时转换导入语句中引用的文件。模块的任意源:允许从文件系统以外的源加载模块(例如,从URL加载模块)。模拟模块:允许在测试时替换为模拟模块。您可以在此处查看完整列表。exportsobjectinpackage.json虽然命名和语法不是最终的,但这里的想法是在package.json文件中的某处有一个对象,允许package中不同组件的“漂亮”入口点。这个package.json是一种可能的实现:{"name":"@myorg/mypackage","version":"1.0.0","type":"module","main":"./dist/index。js","exports":{".":"./src/mypackage.mjs","./data":"./data/somedir/someotherdir/index.mjs"}}开发者可以这样导入数据componentof@myorg/mypackage:import{MyModule}from'@myorg/mypackage/data以包名引用包的根模块当引用同一个包中的另一个模块时,可能会出现大量回溯,如下图示例:importcoolcomponentfrom'../../../coolcomponent/module.js如果实现了这个方案,那么backtrace可以替换为对package.json中定义的包名的引用。新代码如下所示:importcoolcomponentfrom'mypackage/coolcomponent/module.js支持双ESM/CommonJS包允许npm包同时包含CJS和ES模块这对于确保从CommonJS迁移到ES模块后的向后兼容性很有用,例如开发人员友好的路径非常重要。这通常称为“双模式”支持。当前支持双模式的方法是将package.json中现有的主入口点指向CommonJS入口点。如果npm包包含ES模块并且开发人员想要使用它们,则他们需要使用深度导入来访问这些模块(例如import'pkg/module.mjs')。这是一种双模式解决方案,可能随Node.js12LTS一起提供。还有其他双模式支持提案。广泛辩论的提案未能达成共识,其中包括使开发人员更容易发布包含两个独立实现(ESM和CJS)的程序包的选项。更新后的ESM需求提案提出了一种不同的方法,允许开发人员使用require()来解析ES模块。该提案仍处于开放状态,但已被压制,不太可能包含在Node12LTS中。你好ES模块,再见CommonJS?虽然目标是让ES模块最终取代Node.js中的CommonJS模块,但没有人确切知道未来会怎样——也不知道CommonJS模块支持何时会消失。但有一件事是肯定的:Node开发人员花费了大量时间和精力来确保在没有CommonJS的情况下无缝过渡到未来。他们在确保两种模块类型相互操作之间取得平衡,同时尽量不引入太多一旦迁移就无用的新双模式API,并花时间删除Node对CommonJS的支持。那么CommonJS什么时候会从Node.js中移除呢?让我们做出一个大胆且毫无根据的预测:Node18将附带--experimental-no-commonjs-modules,而Node20将完全放弃CommonJS。跨浏览器、服务器和其他JavaScript运行时的模块化JavaScript的未来令人兴奋!
