Node冷门知识点——require('node:path')

3,124 阅读2分钟

背景

今天在看Vite的源码时候,发现有个用法很神奇,代码如下:

import path, { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { readFileSync } from 'node:fs'

const { version } = JSON.parse(
  readFileSync(new URL('../../package.json', import.meta.url)).toString(),
)
...

源码地址在:vite/src/node/cli.ts

因此想要研究一下为什么要用node:path,而不是直接应用 import path from 'path'呢?下面就来好好分析一下。

require机制

首先,我们需要先搞清楚require加载流程:

  • 加载核心模块,如:fs、path等,其实在node运行的时候,已经放到内存中了
  • 加上对应文件后缀,优先级为:test.js > test.json > test.node
  • 搜索路径,如果有指定路径则按照路径去找,如:require(‘./test’) 则在当前目录寻找
  • 如果没有指定路径,则从当前目录下往上去找 node_modules文件夹,然后从文件夹里去遍历寻找对应模块名,如果找不到则到上一层node_modules去找,直到最顶层目录
  • 首次会加载比较慢,后面node.js 会将缓存相关信息到内存避免二次查询

所以当我们加载内置模块的时候,如:require('fs')时候,其实是直接用node内存的变量

Node:ModuleName

接下来我们再到官网中去找一下这个定义,node:module API的使用说明:

可以使用node:前缀来标识核心模块,在这种情况下,它会绕过所需的缓存。例如,require('node:fs')将始终返回内置的fs模块,即使存在该名称所需的.cache条目也是如此。

比较

从上面大概清楚两种用法的实际意义,那么两者分别使用场景呢?或者说Node:ModuleName有什么意义呢?

  • 直接使用require('fs'),需要等node.js将所有核心模块加载完成后才可以使用,这个可以通过 module.builtinModules去判断是否加载完成
  • 使用require('node:fs'),则不需要等待,可以直接调用内置模块,但是却无法使用缓存

因此,如果是对启动速度有要求的功能,建议使用require('node:fs')模式,其他正常调用即可。