Node.js(笔记11) - 模块的加载机制
优先从缓存中加载
模块在第一次加载后会被缓存。这也意味着多次调用 require() 不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块,还是第三方模块,都会优先从缓存加载,提高加载效率。
新建个模块文件 m1.js:
再从 demo.js 中引入模块3次:
在终端执行这个 demo.js :
结果是:
只引入了一次,其实就是缓存了,提高了模块的加载效率;
内置的模块的加载机制
内置模块是由 Node.js 官方提供的模块,内置模块的加载优先级最高(相比于自定义和第三方模块);
例如:require('fs') 始终返回内置的 fs 模块,即使在 node_modules 目录下有名字相同的包也叫 fs。
自定义模块的加载机制
使用 require() 方法加载自定义模块时,必须指定以 ./
或 ../
开头的路径标识符。在加载自定义模块时,如果没有指定 ./
或 ../
这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。
如上例,使用 ./ 路径来加载自定义模块没有问题。
如果改成:
改成这样会被认为是内置或第三方模块,而当前并没有叫这个名字的内置或第三方模块,所以报错。
同时,在使用 require() 引入自定义模块时,如果省略了文件的扩展名,则 node.js 会 按顺序 分别尝试加载以下的文件:
1)按照确切的文件名加进行加载
2)补全 .js
扩展名进行加载;
3)补全 .json
扩展名进行加载;
4)补全 .node
扩展名进行加载;
5)加载失败,终端报错
例如在 demo.js 中加载模块:
同时在当前目录新建4个文件: m1
,m1.js
,m1.json
,m1.node
如果 m1 在,就加载 m1,否则就从 .js、.json、.node 这个顺序依次补全文件名,找到并尝试加载,直到找到,如果都没找到,就报错。
这也就是为什么加载自定义模块时,可以省略扩展名的原因。
第三方模块的加载机制
如果传递给 require() 的模块标识符不是一个内置模块,也没有以 ./ 或 ../ 开头,则 Node.js 会从当前模块的父目录开始,尝试从 /node_modules 文件夹中加载第三方模块。
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如:假设在“C:\Users\name\project\foo.js” 文件里调用了 require('tools'),则 Node.js 会按以下顺序查找:
1)C:\Users\name\project\node_module\tools
2)C:\Users\name\node_module\tools
3)C:\Users\node_module\tools
4)C:\node_module\tools
逐级目录找下去,找到就加载成功,找到根目录还是没有找到,就报错。
目录作为模块
当把目录作为模块标识符,传递给 require() 进行加载的时候,有三种加载方式:
1)在被加载的目录下查找 package.json 文件,如果 main 中定义了入口文件,那会被 require() 加载
2)如果目录里没有 package.json 文件,或 main 入口不存在或无法解析,则 Node.js 会尝试加载目录下的 index.js 文件。
3)如果以上两个都加载失败,则 Node.js 会报错。Error:Cannot find module 'xxx'
做个测试,建如下目录结构:
testm 目录中的3个文件:
index.js
m.js
package.json
在 package.json 中指定了 main 中的文件。
那么从外部的 a.js 加载:
注意,这里只是写了目录,并没有指定文件,所以结果是:
显而易见是 m.js ;
如果删除 package.json 或者不写 main 属性中的入口文件,就会尝试加载 index.js
把 testm 目录中的文件都删除呢?
就会报错。