为了使用ES6模块,我在运行Node应用程序时使用了——experimental-modules标志。
然而,当我使用这个标志时,元变量__dirname不可用。是否有另一种方法来获得与此模式兼容的存储在__dirname中的相同字符串?
为了使用ES6模块,我在运行Node应用程序时使用了——experimental-modules标志。
然而,当我使用这个标志时,元变量__dirname不可用。是否有另一种方法来获得与此模式兼容的存储在__dirname中的相同字符串?
当前回答
正如Geoff指出的那样,下面的代码返回的不是模块的路径,而是工作目录。
import path from 'path';
const __dirname = path.resolve();
使用——experimental-modules
其他回答
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// do not use the following code which is bad for CJK characters
const __filename = new URL('', import.meta.url).pathname;
我创建了这个模块es-dirname,它将返回当前脚本dirname。
import dirname from 'es-dirname'
console.log(dirname())
它既适用于CommonJs脚本,也适用于Windows和Linux上的ES模块。
打开一个问题,如果有一个错误,因为脚本一直在我的项目中工作,但在其他一些情况下可能会失败。因此,不要在生产环境中使用它。这是一个临时的解决方案,我相信Node.js团队在不久的将来会发布一个健壮的方法来实现它。
我使用:
import path from 'path';
const __dirname = path.resolve(path.dirname(decodeURI(new URL(import.meta.url).pathname)));
decodeURI很重要:在我的测试系统的路径中使用空格和其他东西。
Path.resolve()处理相对url。
编辑:
修复支持windows (/C:/…= > C: /…):
import path from 'path';
const __dirname = (() => {let x = path.dirname(decodeURI(new URL(import.meta.url).pathname)); return path.resolve( (process.platform == "win32") ? x.substr(1) : x ); })();
另一个选择
import {createRequire} from 'module'; // need node v12.2.0
const require = createRequire(import.meta.url);
const __dirname = require.resolve.paths('.')[0];
在大多数情况下,使用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');