面试题答案
一键面试require 方法加载模块原理
- 模块查找路径规则:
- 核心模块:Node.js 有一套内置的核心模块,如
http
、fs
等。当使用require('http')
这样的语句加载模块时,Node.js 会首先检查是否为核心模块。如果是,直接加载核心模块,无需在文件系统中查找。 - 文件模块:
- 绝对路径:如果
require
的参数是以/
开头(在 Unix 系统上)或盘符(如C:\
在 Windows 系统上)开头,Node.js 会将其视为绝对路径。例如require('/home/user/myModule.js')
或require('C:\\Users\\user\\myModule.js')
,直接按照此路径加载模块。 - 相对路径:如果参数以
./
或../
开头,Node.js 会相对于调用require
的文件所在目录进行查找。例如在main.js
文件中有require('./subModule.js')
,则会在main.js
所在目录查找subModule.js
文件。如果找不到.js
文件,Node.js 会尝试查找同名的.json
文件(会将其解析为 JSON 对象),如果还找不到,再尝试查找同名的目录。如果找到同名目录,Node.js 会查找该目录下的package.json
文件,看其中是否有main
字段指定入口文件。如果没有package.json
文件或main
字段指定的文件不存在,则会查找该目录下的index.js
文件。 - 自定义模块路径:如果参数不是以
/
、./
或../
开头,Node.js 会在node_modules
目录中查找。Node.js 会从调用require
的文件所在目录开始,逐级向上查找父目录中的node_modules
目录,直到找到模块或者到达文件系统的根目录。例如在project/src/moduleA.js
中require('myModule')
,Node.js 会首先查找project/src/node_modules
,如果没找到,再查找project/node_modules
,依此类推。如果模块是一个包(即包含package.json
文件),package.json
中的main
字段指定了入口文件。如果没有package.json
文件,则会查找index.js
文件。
- 绝对路径:如果
- 核心模块:Node.js 有一套内置的核心模块,如
- 模块缓存机制:
- 缓存目的:为了避免重复加载相同的模块,提高性能。
- 缓存实现:Node.js 内部维护了一个缓存对象,每个模块在第一次被加载后,其导出的内容会被缓存起来。当后续再次
require
同一个模块时(根据模块标识符,即require
的参数,模块标识符在解析路径后是唯一的),Node.js 首先会检查缓存。如果缓存中存在该模块,直接返回缓存中的导出内容,而不会再次执行模块的加载逻辑。 - 缓存范围:缓存是按模块标识符和模块所在的上下文(模块作用域)来维护的。不同的模块作用域中即使
require
相同标识符的模块,也会有不同的缓存实例。例如在不同的文件中require('myModule')
,每个文件的模块作用域内都有自己独立的myModule
缓存。不过,如果是通过符号链接或者相同文件路径引用同一个模块,在同一个进程内会共享缓存。