how node_modules works
node有一个不同于其他平台的机智的模块查找算法, node的查找机制默认是基于本地文件的, 而不是基于一个包含查找路径的数组, 例如 命令行工具中的 $PATH 环境变量那样.
如果你从 /beep/boop/bar.js 进行 require('./foo.js') , node将会寻找 /beep/boop/foo.js 文件, 以 ./ 或者 ../ 开头的路径总是以调用 require() 的文件为基准开始寻找.
但是如果你require一个非相对路径的模块名例如 require('xyz') 从 /beep/boop/foo.js, node 会按照先后顺序搜索下面的路径, 从第一个匹配停止, 没匹配项则报错误.
/beep/boop/node_modules/xyz/beep/node_modules/xyz/node_modules/xyz
对于每一个 xyz 文件夹, 如果存在, node将会首先查看 xyz/package.json 文件的 main 域是否存在. main 域定义了 require() 文件夹时该加载哪一个文件.
例如, 如果 /beep/node_modules/xyz 是第一个匹配项, 而且/beep/node_modules/xyz/package.json 包含了:
{ "name": "xyz", "version": "1.2.3", "main": "lib/abc.js"}
那么 /beep/node_modules/xyz/lib/abc.js 文件的exports值就是 require('xyz') 的返回值. 如果 package.json 文件不存在, 或者 main 域不存在, 会采用默认值 index.js .
/beep/node_modules/xyz/index.js
如果有必要, 你可以深入到一个package内部去加载某一个文件. 例如要加载 dat 这个包的 lib/clone.js 文件, 可以:
var clone = require('dat/lib/clone.js')
递归的node_modules机制将会顺着文件夹层级向上找到第一个 dat 包, 然后从该位置找 lib/clone.js . 这种方法可以在任何可以使用 require*('dat') 的地方使用.
node同样包含一个搜寻路径数组的机智, 但是这种机智已经被废弃, 你应该使用 node_modules 除非 你有很好的理由不去使用它.
node模块查找算法以及npm安装的模块的好处在于, 你永远不会碰到版本冲突的情况, 不同于其他所有的平台, npm 会将每个模块自己的依赖安装到模块内部的你的_modules文件夹.
每一个包都有自己的本地_modules文件夹, 包含了此包的所有依赖, 而且这些依赖的依赖也有自己的node_modules文件夹, 递归向下(recursively all the way down).
这意味着你可以在一个应用中使用同一个库的不同版本, 大大降低了迭代API时需要的协调开销. 这种特性对于像npm这样没有对 包的发布, 组织进行鉴定授权的生态系统非常重要. 每个人都可以简单的按照自己喜欢发布他们的包, 而不必担心他们选择的 版本可能影响同一个应用下其他的依赖.
你可以看到 node_modules 是如何在你本地的应用中工作的. 请查看 avoiding ../../../../../../..部分了解更多.