有个关于模块加载的疑问
发布于 6 年前 作者 bluedrinker 2631 次浏览 来自 问答

深入浅出nodejs里说,“从缓存加载的优化策略是的二次引入时不需要路径分析,文件定位和编译执行过程”, 不用编译可以理解,但为什么不用文件定位,路径分析呢?我看了下缓存的路径都是文件的真实路径,比如"E:\projects\node\test\nodemodules\express\index.js", 第二次require(“expess”)如果不路径分析和文件定位,是如何直接找到这个路径的缓存的呢

9 回复

都已经缓存下来了当然不用再去找一遍了

@dislido 但是它是如何找到缓存的呢,我看到require里储存了缓存对应的真实路径,感觉应该根据真实路径找缓存吧,那找到真实路径应该要定位和分析路径啊

require.cache里已经包含了使用这个模块所需的一切,查找这个缓存的过程和你访问一个普通对象的属性是一样的 只有第一次require这个模块的时候才会通过路径去加载,之后的require只是返回最初加载的那个模块的exports对象而已

@dislido 刚刚看到一篇文章,说require时会先找到真实路径(如果不是原生模块),然后再根据真实路径看是否缓存,这里是阮一峰的文章,但是根据源码看的应该没问题 ,在第三节里

@dislido 也就是说是需要定位和分析的。

@bluedrinker 因为require.cache的结构是这样的

{
  'F:\\test\\test.js': Module { },
  'F:\\test\\test2.js': Module { }
}

所以要用真实路径来获取缓存里的模块 只要计算出来这个路径就可以了,并不需要通过这个路径去找文件,这个路径只是一个id

楼上解释的也不完全对,在根据 require 的入参定位文件全路径的时候也会做缓存

// lib/module.js
Module._resolveFilename = function(request, parent, isMain, options) {
	//...
	// Module._findPath 会优先从缓存读取
	var filename = Module._findPath(request, paths, isMain);
	//...
}

Module._findPath 大致逻辑:

Module._findPath = function(request, paths, isMain) {
if (path.isAbsolute(request)) {
    paths = [''];
  } else if (!paths || paths.length === 0) {
    return false;
  }
  // 根据 require 的入参组装的缓存 key
  var cacheKey = request + '\x00' +
                (paths.length === 1 ? paths[0] : paths.join('\x00'));
  // 如果已经定位过文件的全路径,则存在缓存,直接返回缓存的全路径
  var entry = Module._pathCache[cacheKey];
  if (entry)
    return entry;
  // 如果没有缓存,则按照正常的方式组合 paths 定位具体文件全路径
  //...
}

所以深入浅出的书里描述的并没有问题

回到顶部