点击上方“前端小苑”,选择“置顶公众号”
精品技术文章,热门资讯第一时间送达
本文于2019-04-23由Dr. Axel博士发表在个人网站2ality,全文对Node.js12中模块的新特性做了详细的分析解读。 - 原文:http://2ality.com/2019/04/nodejs-esm-impl.html
- 译者:李初五
Node.js 12(于2019-04-23发布)为ECMAScript模块带来了改进的支持。它实施了去年年底发布的计划的第二阶段。目前,这种支持可以在获得。
继续阅读以了解这种对ECMAScript模块的新支持是如何工作的。
简要说明:文件扩展名.mjs更方便,但.js也可以为ES模块启用。
[h2]1.本篇文章中使用的术语和缩写[/h2]- CommonJS模块(CJS):指的是原始的Node.js模块标准。
- ECMAScript模块(ES模块,ESM):指通过ECMAScript规范标准化的模块。
- package.prop是指prop的package.json。
[h2]2.模块说明符[/h2]模块说明符是标识模块的字符串。它们在CommonJS模块和ES模块中的工作方式略有不同。在我们查看差异之前,我们需要了解模块说明符的不同类别。
[h3]2.1 模块说明符的类别[/h3]在ES模块中,我们区分以下类别的说明符。这些类别源自CommonJS模块。
- /home/jane/file-tools.mjs
复制代码- URL:包括协议(从技术上讲,路径也是URL)。例:
- 'https://example.com/some-module.mjs'
复制代码- 'file:///home/john/tmp/main.mjs'
复制代码- 裸路径:不以点,斜杠或协议开头,并且由没有扩展名的单个文件名组成。例:
- 'the-package/dist/the-module.mjs'
复制代码 [h3]2.2 CommonJS模块说明符[/h3]这是CommonJS处理模块说明符的方式:
- CommonJS不支持URL作为说明符。
- 相对路径和绝对路径按预期处理。
- 您可以将目录foo作为模块加载:
- 如果有文件 foo/index.js
- 如果存在foo/package.json其属性"main"指向模块文件的文件。
- 根据node_modules找到的目录解析裸路径和深度导入路径:
- 与导入模块位于同一目录中
- 在该目录的父级中
- 其它。
- 如果说明符X未引用文件,则系统会尝试使用说明符,和。
此外,CommonJS模块可以访问两个特殊的模块全局变量:
- __filename:包含当前模块的路径。
- __dirname:包含当前模块的父目录的路径。
本节的来源:Node.js文档的“模块”页面。
[h3]2.3 Node.js中的ES模块说明符[/h3]- 除裸路径外,所有说明符都必须引用实际文件。与CommonJS相比,ESM不会添加缺少的文件扩展名。
- 仅file:支持URL说明符的协议。
- 目前不支持绝对路径。作为解决方法,您可以使用以file:///。开头的URL 。
- 相对路径在Web浏览器中被解析 - 相对于当前模块的路径。
- 裸路径相对于node_modules目录进行解析。裸路径引用的模块通过package.main(类似于CJS)指定。
- 深度导入路径也相对于node_modules目录进行解析。
- 不支持导入目录。换句话说,既不能使用package.main(只能用于packages)也不能通过index.*工作。
所有内置的Node.js模块都可以通过裸路径获得,并命名为ESM导出。例如:
- import * as path from 'path';
复制代码- import * as assert from 'assert';
复制代码- [/code][code]assert.equal(
复制代码- path.join('a/b/c', '../d'), 'a/b/d');
复制代码 [h3]2.4 文件扩展名[/h3]Node.js支持以下默认文件扩展名:
- mjs 用于ES模块
- cjs 用于CommonJS模块 文件扩展名.js代表ESM或CommonJS。它是哪一个,取决于package.type,它有两个设置:
- commonjs(默认值):扩展名为.js或没有扩展名的文件被解析为CommonJS。
- module:具有扩展名.js或没有扩展名的文件被解析为ESM。
要查找package.json给定文件,Node.js将在与文件,父目录等相同的目录中进行搜索。
[h3]2.5将非文件源代码解释为CommonJS或ESM[/h3]并非所有Node.js执行的源代码都来自文件。你也可以通过stdin --eval和它发送代码--print。命令行选项--input-type允许您指定如何解释此类代码:
- 作为CommonJS(默认):--input-type=commonjs
- 作为ESM:--input-type=module
[h2]3. 互操作能力[/h2][h3]3.1 从ESM导入CommonJS[/h3]目前,有两种从ES模块导入CommonJS模块的选项。
考虑以下CommonJS模块。
第一个选项是默认导入它(将来可能会添加对命名导入的支持):
- import * as assert from 'assert';
复制代码- [/code][code]import common from './common.cjs'; // default import
复制代码- assert.equal(common.foo, 123);
复制代码 第二种选择是使用createRequire():
- import * as assert from 'assert';
复制代码- [/code][code]import {createRequire} from 'module';
复制代码- const require = createRequire(import.meta.url);
复制代码- [/code][code]const common = require('./common.cjs');
复制代码- assert.equal(common.foo, 123);
复制代码 3.2 从CommonJS导入ESM
如果要从CommonJS模块导入ES模块,可以使用该import()运算符。
例如,采用以下ES模块:
- export const bar = 'abc';
复制代码 这里我们从CommonJS模块导入它:
- const assert = require('assert');
复制代码- [/code][code]async function main() {
复制代码- const es = await import('./es.mjs');
复制代码- assert.equal(es.bar, 'abc');
复制代码 4. 各种其他功能
[h3]4.1 import.meta.url[/h3]鉴于 __filename和 __dirname在ES模块不可用,我们需要一个替代方案。import.meta.url是另一种选择。它包含file:具有绝对路径的URL。例如:
- 'file:///Users/rauschma/my-module.mjs'
复制代码 注意:url.fileURLToPath()用于提取路径 - new URL().pathname并不是总可以正常运行:
- import * as assert from 'assert';
复制代码- import {fileURLToPath} from 'url';
复制代码- [/code][code]//::::: Unix :::::
复制代码- [/code][code]const urlStr1 = 'file:///tmp/with%20space.txt';
复制代码- new URL(urlStr1).pathname, '/tmp/with%20space.txt');
复制代码- fileURLToPath(urlStr1), '/tmp/with space.txt');
复制代码- [/code][code]const urlStr2 = 'file:///home/thor/Mj%C3%B6lnir.txt';
复制代码- new URL(urlStr2).pathname, '/home/thor/Mj%C3%B6lnir.txt');
复制代码- fileURLToPath(urlStr2), '/home/thor/Mjlnir.txt');
复制代码- [/code][code]//::::: Windows :::::
复制代码- [/code][code]const urlStr3 = 'file:///C:/dir/';
复制代码- new URL(urlStr3).pathname, '/C:/dir/');
复制代码- fileURLToPath(urlStr3), 'C:\\dir\\');
复制代码 下一节演示使用import.meta.url和url.fileURLToPath()。
反过来想url.fileURLToPath()是url.pathToFileURL():它将路径转换为文件URL。
[h3]4.2 fs.promises[/h3]fs.promises包含fsAPI 的promisified版本并按预期工作
- import {fileURLToPath} from 'url';
复制代码- import {promises as fs} from 'fs';
复制代码- [/code][code]async function main() {
复制代码- // The path of the current module
复制代码- const pathname = fileURLToPath(import.meta.url);
复制代码- const str = await fs.readFile(pathname, {encoding: 'UTF-8'});
复制代码 4.3 --experimental-json-modules
使用该标志--experimental-json-modules,Node.js将.json文件加载为JSON。以JSON文件为例data.json:
可以从ES模块导入它,如下所示(如果同时使用ESM和JSON模块的标志):
- import * as assert from 'assert';
复制代码- import data from './data.json';
复制代码- [/code][code]assert.deepEqual(
复制代码- {first: "Jane", last: "Doe"});
复制代码 5.npm上的ES模块
目前,npm上的ES模块引用有以下两种方式可以选择:
你不能两者兼顾(深度导入路径是合理的解决方法)。我们正在努力改变这一状况。它可能会通过package.main更强大的功能来完成。
在该功能准备就绪之前,对处理该功能的人员有以下要求:- “在解决之前,请不要发布任何供Node.js使用的ES模块包。”
复制代码 [h2]6.在Node.js上使用ES模块[/h2]从Node.js 12开始,Node.js上使用ES模块有以下选项:
- library:ESM由John-David Dalton维护。esm还支持旧版本的Node.js.
- Flag--experimental-modules
当Node.js 12达到LTS状态时,可能会在2019年10月删除ESM支持的标志。
最后欢迎大家关注公众号前端小苑,我会定期在这里发表原创文章。
更多精选文章
[h1]vue3.0抢先看(附尤雨溪vue分享ppt)[/h1][h1]JS是世界上最好的语言—— 使用Electron开发桌面应用(一)[/h1][h1]轻松搞定时间复杂度[/h1][h1]你能找到心仪的妹子吗?- 时间复杂度进阶[/h1][h1]深入vue - 源码目录及构建过程分析[/h1][h1]深入vue -- Virtual Dom是如何被创建的[/h1][h1]不仅仅是算法--二分查找(binary search)的心路历程[/h1][h1]Node+GitLab实现小程序CI系统[/h1][h1]还在手动维护项目的更新日志吗?那你就OUT了![/h1][h1]小程序第三方框架对比 ( wepy / mpvue / taro )[/h1]
![]()
点击“阅读原文”,查看更多精品文章
点一下你会更好看耶
|
|