require模块的载入策略

NodeJs中模块的分类

  1. 内置模块

    也叫原生模块,核心模块,内置模块在安装node.js的时候就已经编译成二 进制文件(保存在lib目录下),可以直接加载运行(速度较快),部分内置模块,在node.exe这个进程启动的时候就已经默认加载了,所以可以直接使用。
    例如:fs、path、http

  2. 文件模块

    路径模块:也就是用相对路径或者绝对路径引入的。

    即本地包(开发人员自己编写的js文件)

    第三方模块(node_modules)

    即第三方包,比如从npm上下载,也需要加载编译,较慢。比如require('mysql')

  3. 缓存中的模块

    require函数加载文件后会将加载结果缓起来,当下次调用require函数加载时会直接返回已缓存的内容。
    无论是内置模块还是文件模块都可以被缓存

模块查找的优先级

在这里插入图片描述

  1. 文件模块缓存中加载

    尽管原生模块与文件模块的优先级不同,但是都不会优先于从文件模块的缓存 中加载已经存在的模块。

  2. 从原生模块加载

    原生模块的优先级仅次于文件模块缓存的优先级。require方法在解析文件名之后,优先检查模块是否在原生模块列表中。

    以http模块为例,尽管在目录下存在一个http/http.js/http.node/http.json文件,require(“http”)都不会从这些文件中加载,而是从原生模块中加载。

    原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。

  3. 从文件模块加载

    当文件模块缓存中不存在,而且不是原生模块的时候,Node.js会解析require方法传入的参数,并从文件系统中加载实际的文件

require方法接受的参数

  • http、fs、path等,原生模块。(内置模块)
  • /mod或../mod,相对路径的文件模块。(路径模块-开发者自己编写的)
  • /pathtomodule/mod,绝对路径的文件模块。(路径模块-开发者自己编写的)
  • mod,非原生模块的文件模块。(第三方模块,从npm下载的)

module.paths

对于每一个被加载的文件模块,创建这个模块对象的时候,这个模块便会有一个paths属性,其值根据当前文件的路径计算得到.
在这里插入图片描述

可以看出module.paths的生成规则为:

  • 从当前文件目录开始查找node_modules目录;
  • 然后依次进入父目录,查找父目录下的node_modules目录;
  • 依次迭代, 直到根目录下的node_modules目录。

除此之外还有一个全局module path,是当前node执行文件的相对目录 (…/…/lib/node)。

如果在环境变量中设置了HOME目录和NODE_PATH目录的话,整个路径还包含NODE_PATH和HOME目录下的.node_libraries 与.node_modules。

[
    NODE_PATH, 
    HOME/.node_modules,
    HOME/.node_libraries,
    execPath/../../lib/node
]

在这里插入图片描述

require查找策略

  • 第一步:如果没有相对路径的话就先判断是否是内核模块

    如: require(‘fs’)就是在此时加载进来

  • 第二步:如果有相对路径的话,那么就去对应的路径下寻找

    如:require(’./util’)或require(’…/util’)

  • 第三步:如果没有使用相对路径的话,就认为是第三方包(require("demo")),于是开始列举所有可能存在的位置

  • 第四步:对于第二步和第三步举出的目录,依次进行搜索,搜索的方式为

    加上后缀.js|.json|.node去查找文件,找到就返回
    找不到的就把这个路径当成文件夹,在这个文件夹里找package.json里的main属性,找到了就返回这个指定的脚本
    还找不到的直接在这个文件夹里查找index.js|index.json|index.node文件,找到了返回,找不到就下一条路径

【整个文件的查找流程如下:】
在这里插入图片描述
总结:
在Nodejs中模块加载一般会经历3个步骤,路径分析、文件定位、编译执行

按照模块的分类,按照以下顺序进行优先加载:

系统缓存:模块被执行之后会会进行缓存,首先是先进行缓存加载,判断缓存中是否有值。

系统模块:也就是原生模块,这个优先级仅次于缓存加载,部分核心模块已经被编译成二进制,省略了路径分析、文件定位,直接加载到了内存中,系统模块定义在Node.js源码的lib目录下,可以去查看。

文件模块:优先加载.、..、/开头的,如果文件没有加上扩展名,会依次按照.js、.json、.node进行扩展名补足尝试

目录做为模块:这种情况发生在文件模块加载过程中,也没有找到,但是发现是一个目录的情况,这个时候会将这个目录当作一个包来处理,Node这块采用了Commonjs规范,先会在项目根目录查找package.json文件,取出文件中定义的main属性("main": "lib/hello.js")描述的入口文件进行加载,也没加载到,则会抛出默认错误: Error: Cannot find module 'lib/hello.js'

node_modules目录加载:对于系统模块、路径文件模块都找不到,Node.js会从当前模块的父目录进行查找,直到系统的根目录

问题小点

模块循环引用

假设有a.js、b.js两个模块相互引用,会有什么问题?是否为陷入死循环?看以下例子

// a.js
console.log('a模块start');
exports.test = 1;
undeclaredVariable = 'a模块未声明变量'
const b = require('./b');
console.log('a模块加载完毕: b.test值:',b.test);
// b.js
console.log('b模块start');
exports.test = 2;
const a = require('./a');
console.log('undeclaredVariable: ', undeclaredVariable);
console.log('b模块加载完毕: a.test值:', a.test);

启动a.js的时候,会加载b.js,那么在b.js中又加载了a.js,但是此时a.js模块还没有执行完,返回的是一个a.js模块的exports对象未完成的副本给到b.js模块。然后b.js完成加载之后将exports对象提供给了a.js模块
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值