对于文件和包模块,单个文件和文件夹可以匹配到模块名,特别的,算法将尝试匹配一下内容
- <moduleName>.js
- <moduleName>/index.js
- 在<moduleName>/package main中指定的目录/文件
算法文档
每个包通过npm安装的依赖会放在node_modules文件夹下,这就意味着,按照我们刚刚算法的描述,每个包都会有它自己私有的依赖。
myApp ├── foo.js └── node_modules ├── depA │ └── index.js └── depB │ ├── bar.js ├── node_modules ├── depA │ └── index.js └── depC ├── foobar.js └── node_modules └── depA └── index.js
通过看上面的文件夹结构,myApp、depb和depC都依赖depA,但是他们都有自己私有的依赖版本,根据上面所说的算法的规则,当使用require('depA')会根据加载的模块的位置加载不同的文件
- myApp/foo.js 加载的是 /myApp/node_modules/depA/index.js
- myApp/node_modules/depB/bar.js 加载的是 /myApp/node_modules/depB/node_modules/depA/index.js
- myApp/node_modules/depB/depC/foobar.js 加载的是 /myApp/node_modules/depB/depC/node_modules/depA/index.js
resolving算法是保证Node依赖管理的核心部分,它的存在使得即便应用程序拥有成百上千个包的情况下也不会出现冲突和版本不兼容的问题
当我们使用require()时,resolving算法对于我们是透明的,然后,如果需要的话,也可以在模块中直接通过调用require.resolve()来使用
模块缓存(module cache)
每个模块都会在它第一次被require的时候加载和计算,然后随后的require会返回缓存的版本,这一点通过看我们自制的require函数会非常清楚,缓存是提高性能的重要手段,而且他也带来了一些其他的好处
- 使得在模块依赖关系中,循环依赖变得可行
- 它保证了在给定的包中,require相同的模块总是会返回相同的实例
模块的缓存通过变量require.cache暴露出来,所以如果需要的话,可以直接获取,一个很常见的使用场景是通过删除require.cache的key值使得某个模块的缓存失效,但是不建议在非测试环境下去使用这个功能
循环依赖
很多人会认为循环依赖是自身设计的问题,但是这确实是在真实的项目中会发生的问题,所以我们很有必要去弄清楚在Node内部是怎么工作的。然我们通过我们自制的require函数来看看有没有什么问题
定义两个模块
// a.js exports.loaded = false; const b = require('./b.js'); module.exports = { bWasLoaded: b.loaded, loaded: true } // b.js exports.loaded = false; const a = require('./a.js'); module.exports = { aWasLoaded: a.loaded, loaded: true }
内容版权声明:除非注明,否则皆为本站原创文章。