本文于2019-04-23由Dr. Axel博士发表在我的网站2ality,全文对Node.js12中模块的新特性作了详细的分析解读。html
Node.js 12(于2019-04-23发布)为ECMAScript模块带来了改进的支持。它实施了去年年末发布的计划的第二阶段。目前,这种支持能够在experimental-modules
得到。前端
继续阅读以了解这种对ECMAScript模块的新支持是如何工做的。node
简要说明:文件扩展名.mjs更方便,但.js也能够为ES模块启用。npm
模块说明符是标识模块的字符串。它们在CommonJS模块和ES模块中的工做方式略有不一样。在咱们查看差别以前,咱们须要了解模块说明符的不一样类别。json
在ES模块中,咱们区分如下类别的说明符。这些类别源自CommonJS模块。promise
./some/other/module.mjs
../../lib/counter.mjs
复制代码
/home/jane/file-tools.mjs
复制代码
'https://example.com/some-module.mjs'
'file:///home/john/tmp/main.mjs'
复制代码
'lodash'
'the-package'
复制代码
'the-package/dist/the-module.mjs'
复制代码
这是CommonJS处理模块说明符的方式:浏览器
X.js
,X.json
和X.node
。此外,CommonJS模块能够访问两个特殊的模块全局变量:bash
本节的来源:Node.js文档的“模块”页面。async
全部内置的Node.js模块均可以经过裸路径得到,并命名为ESM导出。例如:网站
import * as path from 'path';
import * as assert from 'assert';
assert.equal(
path.join('a/b/c', '../d'), 'a/b/d');
复制代码
Node.js支持如下默认文件扩展名:
mjs 用于ES模块
cjs 用于CommonJS模块 文件扩展名.js表明ESM或CommonJS。它是哪个,取决于package.type,它有两个设置:
commonjs(默认值):扩展名为.js或没有扩展名的文件被解析为CommonJS。
"type": "commonjs"
复制代码
module:具备扩展名.js或没有扩展名的文件被解析为ESM。
"type": "module"
复制代码
要查找package.json给定文件,Node.js将在与文件,父目录等相同的目录中进行搜索。
并不是全部Node.js执行的源代码都来自文件。你也能够经过stdin --eval和它发送代码--print。命令行选项--input-type容许您指定如何解释此类代码:
目前,有两种从ES模块导入CommonJS模块的选项。
考虑如下CommonJS模块。
// common.cjs
module.exports = {
foo: 123,
};
复制代码
第一个选项是默认导入它(未来可能会添加对命名导入的支持):
// es1.mjs
import * as assert from 'assert';
import common from './common.cjs'; // default import
assert.equal(common.foo, 123);
复制代码
第二种选择是使用createRequire():
// es2.mjs
import * as assert from 'assert';
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const common = require('./common.cjs');
assert.equal(common.foo, 123);
复制代码
若是要从CommonJS模块导入ES模块,可使用该import()运算符。
例如,采用如下ES模块:
// es.mjs
export const bar = 'abc';
复制代码
这里咱们从CommonJS模块导入它:
// common.cjs
const assert = require('assert');
async function main() {
const es = await import('./es.mjs');
assert.equal(es.bar, 'abc');
}
main();
复制代码
鉴于 __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';
//::::: Unix :::::
const urlStr1 = 'file:///tmp/with%20space.txt';
assert.equal(
new URL(urlStr1).pathname, '/tmp/with%20space.txt');
assert.equal(
fileURLToPath(urlStr1), '/tmp/with space.txt');
const urlStr2 = 'file:///home/thor/Mj%C3%B6lnir.txt';
assert.equal(
new URL(urlStr2).pathname, '/home/thor/Mj%C3%B6lnir.txt');
assert.equal(
fileURLToPath(urlStr2), '/home/thor/Mjölnir.txt');
//::::: Windows :::::
const urlStr3 = 'file:///C:/dir/';
assert.equal(
new URL(urlStr3).pathname, '/C:/dir/');
assert.equal(
fileURLToPath(urlStr3), 'C:\\dir\\');
复制代码
下一节演示使用import.meta.url和url.fileURLToPath()。
反过来想url.fileURLToPath()是url.pathToFileURL():它将路径转换为文件URL。
fs.promises包含fsAPI 的promisified版本并按预期工做
import {fileURLToPath} from 'url';
import {promises as fs} from 'fs';
async function main() {
// The path of the current module
const pathname = fileURLToPath(import.meta.url);
const str = await fs.readFile(pathname, {encoding: 'UTF-8'});
console.log(str);
}
main();
复制代码
使用该标志--experimental-json-modules,Node.js将.json文件加载为JSON。
以JSON文件为例data.json:
{
"first": "Jane",
"last": "Doe"
}
复制代码
能够从ES模块导入它,以下所示(若是同时使用ESM和JSON模块的标志):
import * as assert from 'assert';
import data from './data.json';
assert.deepEqual(
data,
{first: "Jane", last: "Doe"});
复制代码
目前,npm上的ES模块引用有如下两种方式能够选择:
require('mylib')
import from 'mylib
你不能二者兼顾(深度导入路径是合理的解决方法)。咱们正在努力改变这一情况。它可能会经过package.main更强大的功能来完成。
在该功能准备就绪以前,对处理该功能的人员有如下要求:
“在解决以前,请不要发布任何供Node.js使用的ES模块包。”
复制代码
从Node.js 12开始,Node.js上使用ES模块有如下选项:
library:ESM由John-David Dalton维护。esm还支持旧版本的Node.js.
Flag--experimental-modules
当Node.js 12达到LTS状态时,可能会在2019年10月删除ESM支持的标志。
最后欢迎你们关注公众号前端小苑,我会按期在这里发表原创文章。