ToC
usage
在开始之前,可以先看一下基本用法。
- 如果 X 是内置模块则直接返回模块。
- 内置模块可以通过 module.builtinModules 属性来查看。
如果 X 模块路径是以 ’/’、’./’、‘../’ 开头
- 根据 X 所在的父模块,确定 X 的绝对路径。
- 将 X 当成文件,依次查找下面文件,只要有一个存在,则直接返回文件不再查找,可以通过 module._extensions 属性查看受支持的文件类型列表
- 将 X 当成目录,依次查找下面文件。只要有一个存在就返回,不再继续执行
- X/package.json(main字段)
- X/index.js
- X/index.json
- X/index.node
如果 X 不带路径,比如 ”、 ’.’、’..’ 等。
- 根据 X 所在的父模块,确定 X 可能的安装目录。
- 依次在每个目录中,将 X 当成文件名或目录名加载。
throw module not found.
require
每个实例都有一个 require
方法。
由此可知,require 并不是全局性命令,而是每个模块提供的一个内部方法,也就是说,只有在模块内部才能使用 require 命令(唯一的例外是 REPL 环境)。另外,require 其实内部调用 Module._load 方法。接下来是 Module._load
的源码:
上面代码中,首先解析出模块的绝对路径(filename),以它作为模块的识别符。然后,如果模块已经在缓存中,就从缓存取出;如果不在缓存中,就加载模块。所以 Module._load
的关键步骤是两个:
- Module._resolveFilename():确定模块的绝对路径
- module.load():加载模块
load
确定了模块的绝对路径以后,就可以开始加载模块了。以下是 Module.load
方法的源码:
上面的实例代码中,首先确定了模块的后缀名,不同的后缀对应不同的加载方法。
_compile
代码等同于:
也就是说,模块的加载实质上就是,注入exports、require、module三个全局变量,然后执行模块的源码,然后将模块的 exports 变量的值输出。那么可以根据编译的过程得出 require
方法加载模块时调用的钩子函数。
- _load
- load
- _extensions
- _compile
从上面的执行流程可以知道,在引入了模块以后,是在 _extensions
方法内读取的文件内容,那么在这里面做一点点操作,那么就可以达到修改模块引入结果的效果,就像 ts-node
一样。
- require() 源码解读
- 给大家变个 Node.js 的小魔术