为了使用ES6模块,我在运行Node应用程序时使用了——experimental-modules标志。
然而,当我使用这个标志时,元变量__dirname不可用。是否有另一种方法来获得与此模式兼容的存储在__dirname中的相同字符串?
为了使用ES6模块,我在运行Node应用程序时使用了——experimental-modules标志。
然而,当我使用这个标志时,元变量__dirname不可用。是否有另一种方法来获得与此模式兼容的存储在__dirname中的相同字符串?
当前回答
不管你是否同意使用global,我发现这是记忆和重构现有代码的最简单方法。
在代码执行的早期放置:
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
global.___filename = (path) => {
return fileURLToPath(path);
};
global.___dirname = (path) => {
return dirname(global.___filename(path));
};
然后在任何需要dirname或filename的文件中:
___filename(import.meta.url)
___dirname(import.meta.url)
当然,如果有宏,我就不需要传递import。meta了。Url,也许有改进。
其他回答
正如Geoff指出的那样,下面的代码返回的不是模块的路径,而是工作目录。
import path from 'path';
const __dirname = path.resolve();
使用——experimental-modules
2021年最标准化的方式
import { URL } from 'url'; // in Browser, the URL in native accessible on window
const __filename = new URL('', import.meta.url).pathname;
// Will contain trailing slash
const __dirname = new URL('.', import.meta.url).pathname;
忘记使用join从当前文件创建路径,只使用URL
const pathToAdjacentFooFile = new URL('./foo.txt', import.meta.url).pathname;
const pathToUpperBarFile = new URL('../bar.json', import.meta.url).pathname;
在大多数情况下,使用Node.js的本机(带有ES模块),而不是外部资源,在大多数情况下使用__filename和__dirname是完全不必要的。大多数(如果不是全部)本地读取(流)方法都支持新的URL + import.meta。Url,正如官方文档本身所暗示的那样:
没有__filename或__dirname 不加载JSON模块 没有require.resolve
正如你在方法的描述中看到的,path参数显示了支持的格式,其中包括<URL>,示例:
Method | path param supports |
---|---|
fs.readFile(path[, options], callback) |
<string> , <Buffer> , <URL> , <integer> |
fs.readFileSync(path[, options]) |
<string> , <Buffer> , <URL> , <integer> |
fs.readdir(path[, options], callback) |
<string> , <Buffer> , <URL> |
fs.readdirSync(path[, options]) |
<string> , <Buffer> , <URL> , <integer> |
fsPromises.readdir(path[, options]) |
<string> , <Buffer> , <URL> |
fsPromises.readFile(path[, options]) |
<string> , <Buffer> , <URL> , <FileHandle> |
因此,有了新的URL('<path or file>', import.meta.url),它就解决了问题,而且你不需要处理字符串和创建变量,以便以后连接。
例子:
看看如何在不需要__filename或任何变通方法的情况下读取与脚本相同级别的文件:
import { readFileSync } from 'fs';
const output = readFileSync(new URL('./foo.txt', import.meta.url));
console.log(output.toString());
列出脚本目录下的所有文件:
import { readdirSync } from 'fs';
readdirSync(new URL('./', import.meta.url)).forEach((dirContent) => {
console.log(dirContent);
});
注意:在示例中,我使用同步函数只是为了便于复制和执行。
如果意图是制作一个依赖于第三方的“自己的日志”(或类似的东西),那么手动做一些事情是值得的,但在语言和Node.js中这是不必要的,使用ESMODULES完全可以不依赖__filename和__dirname,因为带有新URL的本地资源已经解决了这个问题。
注意,如果你对在战略时刻使用require之类的东西感兴趣,并且需要主脚本的绝对路径,你可以使用module.createRequire(filename)(仅适用于Node.js v12.2.0 +)结合import.meta.url来加载当前脚本级别以外的级别的脚本,因为这已经有助于避免对__dirname的需求,一个使用import.meta.url和module.createRequire的例子:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
// foo-bar.js is a CommonJS module.
const fooBar = require('./foo-bar');
fooBar();
来源:foo-bar.js:
module.exports = () => {
console.log('hello world!');
};
这类似于不使用“ECMAScript模块”:
const fooBar = require('./foo-bar');
另一个选择
import {createRequire} from 'module'; // need node v12.2.0
const require = createRequire(import.meta.url);
const __dirname = require.resolve.paths('.')[0];
不管你是否同意使用global,我发现这是记忆和重构现有代码的最简单方法。
在代码执行的早期放置:
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
global.___filename = (path) => {
return fileURLToPath(path);
};
global.___dirname = (path) => {
return dirname(global.___filename(path));
};
然后在任何需要dirname或filename的文件中:
___filename(import.meta.url)
___dirname(import.meta.url)
当然,如果有宏,我就不需要传递import。meta了。Url,也许有改进。