韩海Tempest

ResolveFactory浅析

Nov 14, 2023 · 10min

    1、ensureHook 的作用是确保给定的hook存在,并且返回该hook的引用

    内部实现是创建了一个 AsyncSeriesBailHook 的实例(理解 tapable库)。

    resolver.ensureHook("resolve"); // 在解析过程开始时触发,用于解析模块的绝对路径
    resolver.ensureHook("internalResolve"); // 在内部解析过程开始时触发,用于解析内部模块的路径
    resolver.ensureHook("newInternalResolve"); // 在新的内部解析过程开始时触发,用于解析新的内部模块路径
    resolver.ensureHook("parsedResolve"); // 在解析过程中,路径已被解析为请求对象时触发,用于处理解析后的请求对象
    resolver.ensureHook("describedResolve"); // 在解析过程中请求对象已被描述时触发,可以用于处理描述后的请求对象
    resolver.ensureHook("rawResolve"); // 在原始解析过程开始时触发,开始解析一个原始请求
    resolver.ensureHook("normalResolve"); // 在常规解析过程开始时触发,开始解析一个常规的请求。
    resolver.ensureHook("internal"); // 在解析内部模块时触发,可以用于处理内部模块的解析。
    resolver.ensureHook("rawModule"); // 在解析原始模块时触发,可以用于处理原始模块的解析。
    resolver.ensureHook("module"); // 在解析模块时触发,可以用于处理模块的解析
    resolver.ensureHook("resolveAsModule");  // 在将路径解析为模块时触发,可以用于处理模块路径的解析。
    resolver.ensureHook("undescribedResolveInPackage"); // 在解析包内未描述的请求时触发,可以用于处理包内未描述请求的解析。
    resolver.ensureHook("resolveInPackage"); // 在解析包内的请求时触发,可以用于处理包内请求的解析。
    resolver.ensureHook("resolveInExistingDirectory"); // 在解析已存在目录中的请求时触发,可以用于处理已存在目录中请求的解析。
    resolver.ensureHook("relative"); // 在解析相对路径时触发,可以用于处理相对路径的解析。
    resolver.ensureHook("describedRelative"); // 在解析已描述的相对路径时触发,可以用于处理已描述的相对路径的解析
    resolver.ensureHook("directory"); // 在解析目录时触发,可以用于处理目录的解析。
    resolver.ensureHook("undescribedExistingDirectory"); // 在解析未描述的已存在目录时触发,可以用于处理未描述的已存在目录的解析。
    resolver.ensureHook("existingDirectory"); // 在解析已存在目录时触发,可以用于处理已存在目录的解析。
    resolver.ensureHook("undescribedRawFile"); // 在解析未描述的原始文件时触发,可以用于处理未描述的原始文件的解析。
    resolver.ensureHook("rawFile"); // 在解析原始文件时触发,可以用于处理原始文件的解析。
    resolver.ensureHook("file"); // 在解析文件时触发,可以用于处理文件的解析。
    resolver.ensureHook("finalFile"); // 在解析最终文件时触发,可以用于处理最终文件的解析。
    resolver.ensureHook("existingFile"); // 在解析已存在文件时触发,可以用于处理已存在文件的解析。
    resolver.ensureHook("resolved"); // 在解析过程结束时触发,可以用于处理解析结束后的操作。

    基于以上,实现类似 webpack alias 的功能插件:

    // 默认别名映射
    const defaultAliasMap = {
      'src/components/intercity-sellout/index': 'src/components/button/index',
    }
    
    // 获取别名映射
    function getAliasMap (alias) {
      const aliasMap = new Map()
      Object.keys(alias).forEach((key) => {
        aliasMap.set(key, alias[key])
      })
      return aliasMap
    }
    
    class MpxUIResolverAliasPlugin {
      constructor(options = {}) {
        const alias = options.alias ?  Object.assign({}, defaultAliasMap, options.alias) : defaultAliasMap
        this.alias = getAliasMap(alias)
      }
    
      apply(resolver) {
        const target = resolver.ensureHook('resolve');
        resolver.getHook('described-resolve').tapAsync('MpxUIResolverAliasPlugin', (request, resolveContext, callback) => {
          let hasAlias = false; // 用来标识是否匹配到别名
          let aliasRequestPath = this.alias.get(request.request)
          if (aliasRequestPath) {
            hasAlias = true;
            const obj = Object.assign({}, request, { request: aliasRequestPath });
            return resolver.doResolve(target, obj, null, resolveContext, callback);
          }
          if (!hasAlias) {
            return callback(); // 调用回调函数
          }
        });
      }
    }
    
    module.exports = MpxUIResolverAliasPlugin;

    2、内部通过resolver.doResolve方法调用hook,找到订阅对应hook的plugin并执行,后续也是通过resolver.doResolve方法调用后续的hook。

    3、NextPlugin 的作用是推进hook的执行流程,传递数据。

    >
    CC BY-NC-SA 4.0 2021-PRESENT © 韩海Tempest