- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- bench 基准测试运行器
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- env 环境变量
- Error 错误
- events 事件触发器
- fs 文件系统
- FFI 外部函数接口
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- module/typescript TS 模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- sqlite 轻型数据库
- stream 流
- stream/iter 可迭代流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vfs 虚拟文件系统
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- zlib/iter 可迭代压缩
Node.js v26.11.1 文档
- Node.js v26.11.1
- 目录
-
导航
- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- bench 基准测试运行器
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- env 环境变量
- Error 错误
- events 事件触发器
- fs 文件系统
- FFI 外部函数接口
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- module/typescript TS 模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- sqlite 轻型数据库
- stream 流
- stream/iter 可迭代流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vfs 虚拟文件系统
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- zlib/iter 可迭代压缩
虚拟文件系统#>
🌐 Virtual File System
源代码: lib/vfs.js
node:vfs 模块提供了一个虚拟文件系统,具有类似 node:fs 的 API。它适用于测试、夹具、嵌入式资源以及其他需要自包含文件系统而不接触实际文件系统的场景。
🌐 The node:vfs module provides a virtual file system with a node:fs-like API.
It is useful for tests, fixtures, embedded assets, and other scenarios where you
need a self-contained file system without touching the actual file-system.
要访问它:
🌐 To access it:
import vfs from 'node:vfs';const vfs = require('node:vfs');
这个模块只在 node: 方案下可用,而且只有在用 --experimental-vfs 标志启动 Node.js 时才可用。
🌐 This module is only available under the node: scheme, and only when Node.js
is started with the --experimental-vfs flag.
安全#>
🌐 Security
VFS API 不是沙箱、权限系统或访问控制机制。它不会将不受信任的代码与主文件系统或其他 Node.js 功能隔离开来。可以访问 VirtualFileSystem 实例、挂载它、选择它的提供者或向它传递路径的代码都是受信任的应用代码。
🌐 The VFS API is not a sandbox, permission system, or access-control mechanism.
It does not isolate untrusted code from the host file system or from other
Node.js capabilities. Code that can access a VirtualFileSystem instance,
mount it, select its provider, or pass paths to it is trusted application code.
挂载 VFS 只是重定向受支持的 node:fs 调用,这些调用解析的路径位于挂载点下。它并不能阻止代码使用其他路径或其他 Node.js API 来访问进程可用的资源。RealFSProvider 会将 VFS 路径映射到其配置的根目录下,并拒绝解析到该根目录之外的路径,但这个检查不是安全边界。ZipProvider 本身没有真正的文件系统路径可供逃逸;它的条目只存在于归档自己的命名空间中。不要依赖 VFS 来运行不可信的代码;在需要安全边界时,请使用操作系统级别的隔离,例如不同的用户、容器或平台沙箱。
🌐 Mounting a VFS only redirects supported node:fs calls whose resolved paths
are under the mount point. It does not prevent code from using other paths or
other Node.js APIs to access resources available to the process.
RealFSProvider maps VFS paths under its configured root and rejects paths
that resolve outside that root, but that check is not a security boundary.
ZipProvider has no real file-system paths of its own to escape; its
entries only ever exist within the archive's own namespace. Do not rely on VFS
to run untrusted code; use operating-system-level isolation, such as separate
users, containers, or platform sandboxes, when a security boundary is
required.
基本用法#>
🌐 Basic usage
const vfs = require('node:vfs');
const myVfs = vfs.create();
myVfs.mkdirSync('/dir', { recursive: true });
myVfs.writeFileSync('/dir/hello.txt', 'Hello, VFS!');
console.log(myVfs.readFileSync('/dir/hello.txt', 'utf8')); // 'Hello, VFS!'
vfs.create() 默认返回由 MemoryProvider 支持的 VirtualFileSystem 实例。该实例提供同步、基于回调和基于 Promise 的文件系统方法,这些方法的形式与 node:fs API 相同。所有路径都是 POSIX 风格且为绝对路径(以 / 开头)。
默认情况下,文件树对 VFS 实例是私有的。要通过全局 node:fs 模块、require() 和 import 公开它,调用 vfs.mount();再次分离时调用 vfs.unmount()(或者依赖 using 声明)。
🌐 By default, the file tree is private to the VFS instance. To expose
it through the global node:fs module, require(), and import,
call vfs.mount(); call vfs.unmount() (or rely on a
using declaration) to detach again.
vfs.create([provider][, options])#>
providerVirtualProvider 要使用的提供者。默认:new MemoryProvider()。options<Object>emitExperimentalWarning<boolean> 在创建实例时是否发出实验性警告。默认值:true。
- 返回:VirtualFileSystem
方便工厂,相当于 new VirtualFileSystem(provider, options)。
🌐 Convenience factory equivalent to new VirtualFileSystem(provider, options).
const vfs = require('node:vfs');
// Default in-memory provider
const memoryVfs = vfs.create();
// Explicit provider
const realVfs = vfs.create(new vfs.RealFSProvider('/tmp/vfs-root'));
vfs.registerProvider(entry)#>
entry<Object>name<string> 一个简短的标识符,用于诊断。canHandle<Function> 用解析后的路径和它的fs.Stats调用。如果这个提供者应该支持该来源,则返回true。create<Function> 使用解析后的路径及其fs.Stats调用。返回支持源的 VirtualProvider。
注册一个提供者,让 --vfs-load 可以为它识别的源选择,这样 Node.js 没有内置提供者的文件格式仍然可以被挂载。
🌐 Registers a provider that --vfs-load can select for a source it
recognizes, so a file format Node.js has no built-in provider for can still be
mounted.
一个源会被第一个返回 true 的 canHandle() 的提供者认领。注册提供者会在内置提供者之前被咨询,最新注册的优先,并且它们既可以提供目录也可以提供文件,所以注册提供者可以备份、封装或审核任何源。如果没有提供者认领该源,内置提供者会处理它:一个带有 RealFSProvider 的目录,以及一个字节是包含 ZipProvider 的 ZIP 压缩文件的文件。
🌐 A source is claimed by the first provider whose canHandle() returns true.
Registered providers are consulted before the built-in ones, newest
registration first, and are offered directories as well as files, so a
registered provider can back, wrap, or vet any source. If none claims the
source, the built-in providers handle it: a directory with
RealFSProvider, and a file whose bytes are a ZIP archive with
ZipProvider.
在挂载源之前,提供者必须先注册。可以从预先加载了 --require 或 --import 的模块进行注册:
🌐 Providers must be registered before the source is mounted. Register from a
module preloaded with --require or --import:
// provider.js, preloaded with --require
const fs = require('node:fs');
const vfs = require('node:vfs');
const MAGIC = Buffer.from('CUSTOMFMT');
vfs.registerProvider({
name: 'customfmt',
canHandle(path, stats) {
if (!stats.isFile()) return false;
const head = Buffer.alloc(MAGIC.length);
const fd = fs.openSync(path, 'r');
try {
fs.readSync(fd, head, 0, MAGIC.length, 0);
} finally {
fs.closeSync(fd);
}
return head.equals(MAGIC);
},
create(path) {
return new MyCustomProvider(path);
},
});
$ node --experimental-vfs --require ./provider.js \
--vfs-load archive.customfmt
vfs.vfsBase()#>
返回保存每个已挂载虚拟文件系统挂载点的目录,即 path.join(os.devNull, 'vfs')。读取它会列出已挂载的内容;参见 保留的根目录。
🌐 Returns the directory that holds the mount points of every mounted virtual file
system, which is path.join(os.devNull, 'vfs'). Reading it lists what is
mounted; see The reserved root directory.
const vfs = require('node:vfs');
const fs = require('node:fs');
const myVfs = vfs.create();
const mountPoint = myVfs.mount();
fs.readdirSync(vfs.vfsBase()); // The name of every mount point in it
mountPoint.startsWith(vfs.vfsBase()); // true
类:VirtualFileSystem#>
🌐 Class: VirtualFileSystem
VirtualFileSystem 封装了一个 VirtualProvider,并提供了类似 node:fs 的 API。每个实例都会维护自己的文件树。
🌐 A VirtualFileSystem wraps a VirtualProvider and exposes a
node:fs-like API. Each instance maintains its own file tree.
new VirtualFileSystem([provider][, options])#>
providerVirtualProvider 要使用的提供者。默认:new MemoryProvider()。options<Object>emitExperimentalWarning<boolean> 是否发出实验性警告。默认值:true。
vfs.mount()#>
- 返回:<string> 绝对挂载点
挂载虚拟文件系统并返回生成的挂载点。挂载后,可以通过 node:fs 模块访问 VFS 中的文件,并通过 require() 和 import 使用返回挂载点下的路径进行解析。
🌐 Mounts the virtual file system and returns the resulting mount point.
After mounting, files in the VFS can be accessed through the
node:fs module and resolved through require() and import
using paths under the returned mount point.
挂载点总是存在于一个保留的命名空间中,该命名空间不能有子文件系统条目,因此虚拟路径永远不会与真实路径混淆或重叠。挂载点是从 vfs.mount() 返回的内容或从 vfs.mountPoint 获取的,所有已挂载文件系统的挂载点可以通过读取 保留根目录 来列出,其路径由 vfs.vfsBase() 返回。该目录中挂载点的名称是在运行时分配的,所以不应该去构造或硬编码它。
🌐 Mount points always live inside a reserved namespace that cannot have child file system entries,
so virtual paths never conflate with (or shadow) real paths. A mount point is obtained from what
vfs.mount() returns or from vfs.mountPoint, and the mount points of all mounted file
systems can be listed by reading the reserved root directory, whose path vfs.vfsBase()
returns. The name of a mount point within that directory is assigned at runtime, so it is not
something to construct or hard-code.
const vfs = require('node:vfs');
const fs = require('node:fs');
const myVfs = vfs.create();
myVfs.writeFileSync('/data.txt', 'Hello');
const mountPoint = myVfs.mount();
// e.g. '/dev/null/vfs/0'
fs.readFileSync(`${mountPoint}/data.txt`, 'utf8'); // 'Hello'
像任何挂载点一样,挂载点不能被移除或重命名,也不能通过将其他东西重命名到它上面来替换:fs.rmdir() 和 fs.rename() 会因 EBUSY 失败。对挂载点进行递归 fs.rm() 会在以相同方式失败之前清空文件系统。
🌐 Like any mount point, the mount point cannot be removed or renamed, nor
replaced by renaming something else onto it: fs.rmdir() and
fs.rename() fail with EBUSY. A recursive fs.rm() of the mount
point empties the file system before failing the same way.
每个 VirtualFileSystem 实例最多只能同时挂载一次。尝试挂载已挂载的实例会抛出 ERR_INVALID_STATE。由于每个实例都挂载在自己独立的每层命名空间中,不同实例的挂载永远不会重叠。
🌐 Each VirtualFileSystem instance may be mounted at most once at a
time. Attempting to mount an already-mounted instance throws
ERR_INVALID_STATE. Because each instance mounts inside its own
per-layer namespace, mounts from different instances can never
overlap.
VFS 支持 显式资源管理 提案。使用 using 声明,在离开作用域时自动卸载:
🌐 The VFS supports the Explicit Resource Management proposal. Use
a using declaration to unmount automatically when leaving scope:
const vfs = require('node:vfs');
const fs = require('node:fs');
let mountPoint;
{
using myVfs = vfs.create();
myVfs.writeFileSync('/data.txt', 'Hello');
mountPoint = myVfs.mount();
fs.readFileSync(`${mountPoint}/data.txt`, 'utf8'); // 'Hello'
} // VFS is automatically unmounted here
fs.existsSync(`${mountPoint}/data.txt`); // false
vfs.unmount()#>
卸载虚拟文件系统。卸载后,虚拟文件将无法通过 node:fs、require() 或 import 访问。同一个实例可以通过调用 mount() 再次挂载。
🌐 Unmounts the virtual file system. After unmounting, virtual files
are no longer reachable through node:fs, require(), or import.
The same instance may be mounted again by calling mount().
这个方法是幂等的:在没有挂载的 VFS 上调用 unmount() 不会有任何效果。
🌐 This method is idempotent: calling unmount() on a VFS that is not
currently mounted has no effect.
vfs.mounted#>
挂载 VFS 时为 true;否则为 false。
vfs.mountPoint#>
当前挂载点的绝对字符串(上一次 vfs.mount() 调用返回的值),如果 VFS 没有挂载,则为 null。
🌐 The current mount point as an absolute string (the value returned by
the last vfs.mount() call), or null when the VFS is not
mounted.
vfs.mountPointURL#>
当前挂载点作为 file: URL 字符串(vfs.mountPoint 路径经过 url.pathToFileURL() 转换),或者当 VFS 未挂载时为 null。
🌐 The current mount point as a file: URL string (the vfs.mountPoint
path converted with url.pathToFileURL()), or null when the VFS
is not mounted.
这是为了方便使用基于 URL 的 API(例如动态 import())来访问挂载的文件:
🌐 This is a convenience for addressing mounted files with URL-based
APIs such as dynamic import():
import vfs from 'node:vfs';
const myVfs = vfs.create();
myVfs.writeFileSync('/mod.mjs', 'export const value = 42;');
myVfs.mount();
const { value } = await import(`${myVfs.mountPointURL}/mod.mjs`);
console.log(value); // 42
myVfs.unmount();
vfs.provider#>
- VirtualProvider
支持此 VFS 实例的提供者。
🌐 The provider backing this VFS instance.
vfs.readonly#>
true 当底层提供者是只读时。
应用接口#>
🌐 APIs
VirtualFileSystem 实现了以下方法,它们的签名与对应的 [node:fs][node:fs] 方法相同:
同步 API#>
🌐 Synchronous API
existsSync(path)statSync(path[, options])lstatSync(path[, options])readFileSync(path[, options])writeFileSync(path, data[, options])appendFileSync(path, data[, options])readdirSync(path[, options])mkdirSync(path[, options])rmdirSync(path)unlinkSync(path)renameSync(oldPath, newPath)copyFileSync(src, dest[, mode])realpathSync(path[, options])readlinkSync(path[, options])symlinkSync(target, path[, type])accessSync(path[, mode])rmSync(path[, options])truncateSync(path[, len])ftruncateSync(fd[, len])linkSync(existingPath, newPath)chmodSync(path, mode)chownSync(path, uid, gid)lchownSync(path, uid, gid)utimesSync(path, atime, mtime)lutimesSync(path, atime, mtime)mkdtempSync(prefix)opendirSync(path[, options])openAsBlob(path[, options])- 文件描述符操作:
openSync、closeSync、readSync、writeSync、fstatSync - 直播通道:
createReadStream、createWriteStream - 观察者:
watch、watchFile、unwatchFile
回调接口#>
🌐 Callback API
readFile、writeFile、stat、lstat、readdir、realpath、readlink、access、open、close、read、write、rm、fstat、truncate、ftruncate、link、mkdtemp、opendir。每个都接受一个 Node.js 风格的回调 (err, ...result) => {}。
Promise API#>
vfs.promises 暴露了基于 Promise 的变体:
const vfs = require('node:vfs');
async function example() {
const myVfs = vfs.create();
await myVfs.promises.writeFile('/file.txt', 'hello');
const data = await myVfs.promises.readFile('/file.txt', 'utf8');
return data;
}
example();
promise 命名空间对应 fs.promises,并且包括 readFile、writeFile、appendFile、stat、lstat、readdir、mkdir、rmdir、unlink、rename、copyFile、realpath、readlink、symlink、access、rm、truncate、link、mkdtemp、chmod、chown、lchown、utimes、lutimes、open、lchmod 和 watch。
🌐 The promise namespace mirrors fs.promises and includes readFile,
writeFile, appendFile, stat, lstat, readdir, mkdir, rmdir,
unlink, rename, copyFile, realpath, readlink, symlink,
access, rm, truncate, link, mkdtemp, chmod, chown, lchown,
utimes, lutimes, open, lchmod, and watch.
保留的根目录#>
🌐 The reserved root directory
只要挂载了任何虚拟文件系统,通过 node:fs 就可以读取保存挂载点的目录。vfs.vfsBase() 返回它的路径,path.join(os.devNull, 'vfs')。它包含每个已挂载文件系统的目录,目录名就像它的 vfs.mountPoint 的最后一段一样。
🌐 While any virtual file system is mounted, the directory that holds the mount
points can be read through node:fs. vfs.vfsBase() returns its path,
path.join(os.devNull, 'vfs'). It contains a directory for every mounted file
system, named like the last segment of its vfs.mountPoint.
const vfs = require('node:vfs');
const fs = require('node:fs');
const path = require('node:path');
const root = vfs.vfsBase();
const assets = vfs.create();
assets.writeFileSync('/logo.svg', '<svg/>');
const mountPoint = assets.mount();
const name = path.basename(mountPoint);
fs.readdirSync(root); // [ name ]
fs.readdirSync(root, { recursive: true }); // [ name, `${name}/logo.svg` ]
根目录本身是只读的。创建、删除或更改其条目会失败并显示 EROFS,而其条目所指向的文件系统可以像平常一样写入。当没有挂载任何东西时,根目录不存在。
🌐 The root directory itself is read-only. Creating, removing, or changing its
entries fails with EROFS, while the file systems its entries lead to can be
written to as usual. When nothing is mounted, the root directory does not exist.
模块加载器集成#>
🌐 Module loader integration
一旦挂载了 VirtualFileSystem,挂载点下的路径就会参与模块解析和加载。require() 和 require.resolve() 使用的 CommonJS 解析算法,以及 import 和 import.meta.resolve() 使用的 ES 模块解析算法 都保持不变;相反,这些算法执行的每个文件系统操作都会在被探测的路径上进行派发:挂载点下的路径由拥有它的 VFS 提供服务,其他路径则由真实文件系统提供服务。因此,从 VFS 提供的文件表现得就像一流的模块。
🌐 Once a VirtualFileSystem is mounted, paths under the mount point
participate in module resolution and loading. The CommonJS
resolution algorithm used by require() and
require.resolve() and the ES modules resolution algorithm
used by import and import.meta.resolve() are unchanged;
instead, every file system operation those algorithms perform is
dispatched on the path being probed: paths under a mount point are
served by the owning VFS, and all other paths are served by the real
file system. Files served from the VFS therefore behave as
first-class modules.
因为挂载路径存在于一个保留的命名空间中,这个命名空间在磁盘上不存在,所以任何给定的路径要么由一个 VFS 提供,要么由真实文件系统提供,但不会同时由两者提供。两者之间没有搜索顺序或回退机制:如果挂载点下的路径在 VFS 中不存在,解析将以 ENOENT 失败,不会去检查磁盘,并且挂载层永远不会覆盖真实目录。
🌐 Because mounted paths live in a reserved namespace that cannot exist
on disk, any given path is served either by exactly one VFS or by
the real file system, never both. There is no search order or
fallback between the two: if a path under a mount point does not
exist in the VFS, resolution fails with ENOENT without consulting
the disk, and a mounted layer never shadows a real directory.
为了解析的目的,挂载点的行为类似于文件系统根目录:package.json。作用域查找和 正在从 node_modules 加载文件夹 会在挂载点停止。例如,当 ${mountPoint}/foo/bar/main.cjs 调用 require('baz') 时,查找过程如下:
🌐 For resolution purposes the mount point behaves as a file system
root: package.json scope lookups and loading from node_modules
folders stop at the mount point. For example, when
${mountPoint}/foo/bar/main.cjs calls require('baz'), the lookup
goes through:
${mountPoint}/foo/bar/node_modules/baz${mountPoint}/foo/node_modules/baz${mountPoint}/node_modules/baz- 如果设置了
$NODE_PATH,$NODE_PATH中列出的文件夹 $HOME/.node_modules/baz$HOME/.node_libraries/baz$PREFIX/lib/node/baz
最后四个条目是 全局文件夹,它们属于传统的 CommonJS 行为,并不适用于 import。绝对标识符可以在任意方向跨越边界:真实文件系统上的模块可以 require() 一个挂载路径,而虚拟模块可以 require() 一个真实的模块。
🌐 The last four entries are the global folders, which are legacy
CommonJS behavior and do not apply to import. Absolute specifiers
may cross the boundary in either direction: a module on the real
file system can require() a mounted path, and a virtual module can
require() a real one.
const vfs = require('node:vfs');
const myVfs = vfs.create();
myVfs.mkdirSync('/lib');
myVfs.writeFileSync('/lib/greet.js', 'module.exports = () => "hi";');
myVfs.writeFileSync(
'/lib/package.json', '{"main": "./greet.js"}');
const mountPoint = myVfs.mount();
const greet = require(`${mountPoint}/lib`);
console.log(greet()); // 'hi'
myVfs.unmount();
对于 ECMAScript 模块,在将挂载路径传递给动态 import() 时使用 file: URL。vfs.mountPointURL 以这种形式提供挂载点;这可以让 VFS 导入在 Windows 上保持可移植,因为挂载路径使用 Windows 路径语法。
🌐 For ECMAScript modules, use file: URLs when passing mounted paths
to dynamic import(). vfs.mountPointURL provides the mount
point in that form; this keeps VFS imports portable on Windows,
where mounted paths use Windows path syntax.
import vfs from 'node:vfs';
const myVfs = vfs.create();
myVfs.writeFileSync('/mod.mjs', 'export const value = 42;');
myVfs.mount();
const { value } = await import(`${myVfs.mountPointURL}/mod.mjs`);
console.log(value); // 42
myVfs.unmount();
从挂载的虚拟文件系统(VFS)加载的 CommonJS 模块通过以挂载点开头的 VFS 路径来识别。这可以在模块中的 __filename 和 __dirname,或者涉及 VFS 模块函数的错误堆栈跟踪中看到。VFS 中的 ES 模块也通过其 VFS 路径的 file: URL 来识别,这同样可以在例如 import.meta.url 中看到。
🌐 CommonJS modules loaded from a mounted VFS are identified by their VFS paths
that start with the mount point. This is reflected in, for example, __filename and
__dirname in the module, or the errors stack traces involving functions from
the VFS modules. ES modules in the VFS are similarly identified by the file: URL of
their VFS paths and this is reflected in e.g. import.meta.url.
就像从真实文件系统加载的模块一样,从 VFS 加载的模块在第一次加载时也会被缓存。当使用 require() 或 import() 多次加载属于挂载 VFS 的绝对路径或 URL 时,模块只会被加载一次,之后的调用都会返回同一个实例。
🌐 Like modules loaded from the real file system, modules loaded from the VFS are
cached on the first load. When require() or import() is used to load an absolute
path or URL that falls under the mounted VFS multiple times, the module is only loaded
once and subsequent calls return the same instance.
调用 vfs.unmount() 会使从挂载点加载的模块失效:在重新创建的挂载下对某个路径进行后续的 require() 或 import 操作,会从新挂载的虚拟文件系统重新读取文件,而不是返回旧的模块。从其他虚拟文件系统实例或真实文件系统加载的模块不受影响。
🌐 Calling vfs.unmount() invalidates the modules that were loaded
from the mount point: a subsequent require() or import of a path
under a re-created mount re-reads the file from the newly mounted
VFS rather than returning a stale module. Modules loaded from other
VFS instances or from the real file system are unaffected.
挂载和卸载不会停止已经开始的任何模块执行,也不会使已经从 VFS 模块中生成的任何对象失效。就像真实文件系统中的模块一样,调用者需要自己负责在虚拟文件系统中加载模块时避免删除或使模块失效。
🌐 Mounting and unmounting do not stop any module execution that is already started, or invalidate any objects materialized from VFS modules that are already executed. As with modules in the real file system, the callers are responsible for avoiding removal or invalidation of modules in the virtual file system while they are being loaded.
存储在挂载的虚拟文件系统(VFS)中的本地插件(.node 文件)也可以被 require()。操作系统的动态加载器无法打开虚拟路径,因此插件的字节会从 VFS 读取,并从一个私有的、自清理的临时镜像加载。实际文件系统上的插件不受影响,可以直接加载。
🌐 Native addons (.node files) stored in a mounted VFS can be require()d as
well. The operating system's dynamic loader cannot open a virtual path, so the
addon's bytes are read from the VFS and loaded from a private, self-cleaning
temporary image instead. Addons on the real file system are unaffected and
load directly.
通过 ffi.dlopen()(或 new ffi.DynamicLibrary())打开的共享库工作原理相同:在挂载的 VFS 中检测到库路径,从 VFS 中读取其字节,然后库从一个私有的、自清理的镜像中加载,同时 library.path 继续报告虚拟路径。真实文件系统上的库则直接加载。
🌐 Shared libraries opened through ffi.dlopen() (or
new ffi.DynamicLibrary()) work the same way: a library path inside a
mounted VFS is detected, its bytes are read from the VFS, and the library is
loaded from a private, self-cleaning image while library.path keeps
reporting the virtual path. Libraries on the real file system load directly.
用于单个可执行应用#>
🌐 Use with Single Executable Applications
当以 单一可执行应用 运行,并使用 "useVfs": true 在 SEA 配置下构建时,打包的资源会自动以只读虚拟文件系统的形式挂载,注入的主脚本会从挂载点的根目录执行。无需额外设置。由于挂载点是保留的且在运行时选择,打包的代码通过相对于 __dirname 的路径和相对的 require() 调用来访问资源,而不是通过固定路径:
🌐 When running as a Single Executable Application built with
"useVfs": true in the SEA configuration, the bundled assets are
automatically mounted as a read-only virtual file system and the injected
main script is executed from the root of the mount. No additional setup is
required. Since the mount point is reserved and chosen at runtime, bundled
code accesses the assets through __dirname-relative paths and relative
require() calls rather than through a fixed path:
// In the SEA main script, __dirname is the root of the mounted assets.
const fs = require('node:fs');
const path = require('node:path');
const config = JSON.parse(
fs.readFileSync(path.join(__dirname, 'config.json'), 'utf8'));
const template = fs.readFileSync(
path.join(__dirname, 'templates/index.html'), 'utf8');
支持 ESM 入口点("mainFormat": "module"):主模块通过 ESM 加载器从挂载点内部加载,import.meta.dirname 指向挂载根目录。
🌐 ESM entry points ("mainFormat": "module") are supported: the main module
is loaded from inside the mount through the ESM loader, and
import.meta.dirname points at the mount root.
"useVfs" 不能和 "useSnapshot" 或 "useCodeCache" 一起使用。
如果检测到任何一种组合,SEA 配置解析器会报错。
与其列出单独的 "assets",SEA 配置可以将 "vfsArchive" 指向一个预先构建的 ZIP 压缩包;然后挂载由嵌入式压缩包上的 ZipProvider 支持,并且每个文件在被读取时会被解压。详情见 从 ZIP 压缩包提供资源。
🌐 Instead of listing individual "assets", the SEA configuration can point
"vfsArchive" at a prebuilt ZIP archive; the mount is then backed by a
ZipProvider over the embedded archive, and each file is inflated when
it is read. See Serving the assets from a ZIP archive for details.
查看 单一可执行应用 文档,了解更多关于使用资源创建 SEA 构建的信息。
🌐 See the Single Executable Application documentation for more information on creating SEA builds with assets.
类:VirtualProvider#>
🌐 Class: VirtualProvider
所有 VFS 提供者的基类。子类实现基本操作(比如 open、stat、readdir、mkdir、rmdir、unlink、rename 等),并继承派生方法的默认实现(比如 readFile、writeFile、exists、copyFile、access 等)。
🌐 The base class for all VFS providers. Subclasses implement the essential
primitives (such as open, stat, readdir, mkdir, rmdir, unlink,
rename, etc.) and inherit default implementations of the derived
methods (such as readFile, writeFile, exists, copyFile, access, etc.).
能力标志#>
🌐 Capability flags
provider.readonly<boolean> 默认:false。provider.supportsSymlinks<boolean> 默认:false。provider.supportsWatch<boolean> 默认:false。
创建自定义提供者#>
🌐 Creating custom providers
const { VirtualProvider } = require('node:vfs');
class StaticProvider extends VirtualProvider {
get readonly() { return true; }
statSync(path) { /* ... */ }
openSync(path, flags) { /* ... */ }
readdirSync(path, options) { /* ... */ }
// ...
}
基类会对任何未被重写的原始类型抛出 ERR_METHOD_NOT_IMPLEMENTED,并且拒绝来自 readonly 提供者的 EROFS 写入。
🌐 The base class throws ERR_METHOD_NOT_IMPLEMENTED for any primitive
that has not been overridden, and rejects writes from a readonly
provider with EROFS.
类:MemoryProvider#>
🌐 Class: MemoryProvider
默认的内存提供程序。在 Map 支持的树结构中存储文件、目录和符号链接,支持符号链接(supportsSymlinks === true),并支持监视(supportsWatch === true)。
🌐 The default in-memory provider. Stores files, directories, and symbolic
links in a Map-backed tree, supports symlinks (supportsSymlinks === true), and supports watching (supportsWatch === true).
memoryProvider.setReadOnly()#>
将提供程序锁定为只读模式。之后通过任何使用此提供程序的 VirtualFileSystem 进行写入都会抛出 EROFS。无法将提供程序恢复为可写模式。
🌐 Locks the provider into read-only mode. Subsequent writes through any
VirtualFileSystem using this provider throw EROFS. There is no
way to revert the provider to writable.
const vfs = require('node:vfs');
const provider = new vfs.MemoryProvider();
const myVfs = vfs.create(provider);
myVfs.writeFileSync('/seed.txt', 'initial');
provider.setReadOnly();
myVfs.writeFileSync('/x.txt', 'fail'); // throws EROFS
类:RealFSProvider#>
🌐 Class: RealFSProvider
一个提供者,它封装一个目录(即实际文件系统上的目录),并通过 VFS API 暴露其内容。所有 VFS 路径都是相对于根目录解析的,并且会验证是否保持在根目录内;解析到根目录外的符号链接会被拒绝。这种路径映射不是沙箱或访问控制机制。
🌐 A provider that wraps a directory (i.e. one on the actual file system) and exposes its contents through the VFS API. All VFS paths are resolved relative to the root and verified to stay inside it; symbolic links resolving outside the root are rejected. This path mapping is not a sandbox or access-control mechanism.
new RealFSProvider(rootPath)#>
rootPath<string> 要用作根目录的绝对文件系统路径。必须是非空字符串。
const vfs = require('node:vfs');
const realVfs = vfs.create(new vfs.RealFSProvider('/tmp/vfs-root'));
realVfs.writeFileSync('/file.txt', 'hello'); // writes /tmp/vfs-root/file.txt
realFSProvider.rootPath#>
作为根使用的已解析绝对路径。
🌐 The resolved absolute path used as the root.
类:ZipProvider#>
🌐 Class: ZipProvider
一个提供者,通过 VFS API 暴露 ZIP 压缩文件的条目 —— 无论是 zlib.ZipBuffer(内存中)还是 zlib.ZipFile(磁盘上)。provider.readonly 反映了压缩文件自身的 zipFile.writable 标志:ZipBuffer 总是可写的,而 ZipFile 只有在用 writable: true 打开时才可写。
🌐 A provider that exposes the entries of a ZIP archive - either a
zlib.ZipBuffer (in memory) or a zlib.ZipFile (on disk) - through
the VFS API. provider.readonly reflects the archive's own
zipFile.writable flag: a ZipBuffer is always writable, and a
ZipFile is writable only when opened with { writable: true }.
目录既可以显式识别(名称以 / 结尾的条目),也可以隐式识别(任何以 "<dir>/" 开头的条目名称),并且可以通过 readdir() 列出,包括使用 { recursive: true },无论哪种方式。因为 ZIP 成员不能就地编辑或读取——只能完全写入或完全解压——所以文件在仅用于写入时,只有在句柄关闭时,其内容才会提交(作为新的归档条目)。
每个方法都有一个同步对应方法(openSync()、statSync()、readdirSync() 等等),由同样完整的同步界面 zlib.ZipBuffer/zlib.ZipFile 提供支持。像那些方法一样,这里的同步方法会阻塞 Node.js 事件循环和后续的 JavaScript 执行,直到操作完成——包括任何压缩/解压的过程。
🌐 Every method has a synchronous counterpart (openSync(), statSync(),
readdirSync(), and so on), backed by the equally complete synchronous
surface zlib.ZipBuffer/zlib.ZipFile expose. As with those, the
synchronous methods here block the Node.js event loop and further JavaScript
execution until the operation - including any deflate/inflate pass -
completes.
const vfs = require('node:vfs');
const zlib = require('node:zlib');
const { readFileSync } = require('node:fs');
async function main() {
const zip = new zlib.ZipBuffer(readFileSync('archive.zip'));
const archiveVfs = vfs.create(new vfs.ZipProvider(zip));
console.log(await archiveVfs.promises.readdir('/'));
await archiveVfs.promises.writeFile('/new.txt', 'hello');
}
main();
new ZipProvider(source)#>
sourcezlib.ZipBuffer|zlib.ZipFile 一个已经打开的档案。
实现细节#>
🌐 Implementation details
Stats 对象#>
🌐 Stats objects
VFS Stats 对象是真正的 fs.Stats 实例(当请求 { bigint: true } 时则为 fs.BigIntStats)。它们的字段使用的是合成但稳定的值:
🌐 VFS Stats objects are real instances of fs.Stats (or
fs.BigIntStats when { bigint: true } is requested). Their
fields use synthetic but stable values:
dev是4085(VFS 设备 ID)。ino在每个过程里都是单调递增的。blksize是4096。blocks是Math.ceil(size / 512)。- 时间默认显示条目创建/最后修改的时刻。