涨薪面技:写个 enhanced-resolve 插件(5)

118 阅读3分钟

一、前文回顾

受限于篇幅,上文并没有完全完成整个流水线的注册工作,之所以不在一篇写完也是不想给各位读者带来过大的压力!

我们大致回顾一下从今天的流水线内容:

  1. resolve 开始解析:注册 UnsafeCachePlugin、ParsePlugin 插件;
  2. paresed-resolve:request 解析阶段;
  3. described-resolve 描述文件已解析;
  4. raw-resolve: 原始解析阶段,处理 alias/aliasFields/extension/extensionAlias;

二、流水线注册(2)

这里接着上文未尽的流水线注册内容:

2.1 // normal-resolve: 普通解析阶段

  1. 判断 preferRealative 配置项,默认 false,如果为 true,则注册 JoinRequestPlugin 插件,该插件的 source 钩子是 after-normal-resolve,target 钩子是 relative。preferRealative 是偏好相对路径,其作用是把不写路径的的 request ,比如 import logo from 'logo1'; logo1 就会被当成 './logo1.js' 而不是当成 node_modules/logo1 这个模块文件夹解析。上 webpack.config.js.resolve.preferRelative 传送门

  2. 注册 ConditionalPlugin 插件,条件 { module: true },source 钩子为 after-normal-resolve,target 钩子为 raw-resolve 钩子。作为模块解析?

  3. 注册 ConditionalPlugin 插件,条件 { internal: true },source 钩子为 after-normal-resolve,target 钩子为 internal 钩子。作为内部 import 解析?

  4. 判断如果 preferAbsolute 配置项,默认 false,如果为 true 注册 JoinRequestPlugin 拼接请求,source 钩子为 after-normal-resolvetarget 钩子relative。preferAbsolute 是指在解析的过程中优先 webpack.config.js.resolve.roots 配置的包管理路径;

  5. 判断 roots.length > 0,即 webpack.config.js.resolve.roots 配置不为空,就注册 RootsPlugin 插件,source 钩子after-normal-resolvetarget 钩子relative。webpack.config.js.resolve.roots 是 webpack 解析包的根路径,默认是 webpack 配置的 context 目录,上 resolve.roots 传送门

  6. 判断 !preferRelative && !preferAbsolute,如果条件成立表示无绝对路径偏好或者相对路径偏好,此时再次注册 JoinRequestPlugin,source 钩子after-normal-resolve,target 钩子 relative;这里有个问题?为啥 preferAbsolute 和 preferRelative 成立时注册 JoinRequestPlugin,都不满足的时候也要注册 JoinRequestPlguin?

2.2 // internal: 内部解析阶段

遍历 importsFields 字段,为每个字段注册 ImportsFieldPlugin 插件,source 钩子 internal,target 钩子 internal-resolve。importsFields 是用于提供包内部request 的 package.json 中的字段名,上 webpack.config.js.resolve.importsFields 传送门。值得注意的是官方文档上写的 importsFields 配置似乎有点问题,应该是:importFields: ["imports"],imports 对应的就是 package.json.imports 字段,而不是配置 browser/node/module 这样的值。。。

就是说有些包内部的某些模块是可以直接给当前包内部解析用的比如有个 npm 包叫做 "qiang-sheng-group",里面有两个模块 "tangxiaolong.js""tangxiaohu.js" 两个模块除了强哥家里人谁也不敢用,这个时候这个包的 package.json 里面就要写上:

    {
      "name": "qiang-sheng-group",
      "main": "./lib/gaoqiqiang.js",
      "imports": {
        "#xiaohu": "./lib/tangxiaohu.js",
        "#xiaolong": "./lib/tangxiaolong.js"
      }
    }

这个时候 "gaoqiqiang.js" 里面就可以通过以下方式调用:

import TangXiaolong from '#xiaolong';
console.log(Tangxiaolong)

其中 "#xiaolong" 就会发起内部 request

imports 字段更多参考文献(反正我是看了TM好久才看明白的,原文赶紧上车李有田主任的车):

  1. webpack 指南:Package export
  2. Node.js: package.json "imports" 字段

See You Again

2.3 // raw-module 阶段

  1. 遍历 exportsFields 字段,为每个字段注册 SelfReferencePlugin 插件。source 钩子 raw-module,target 钩子 resolve-as-module。exportsFileds 是借助 package.json.exports(或其他字段)改写原有的 package.json 通过 main 字段指定入口等行为的,除了可以改写 main 主入口,还可以改写 pgk/sub/path 这种子路径,上 [webpack.config.js.resolve.exportsFields 传送门]。(webpack.js.org/configurati…)

  2. 遍历 modules 配置项,配置不同插件插件的 source 钩子为 raw-module,target 钩子为 module。webpack.config.js.resolve.modules 可以理解成一组包管理管理的目录,就是 node_modules 同功能的目录,告诉 webpack 从哪个路径解析包。webpack.config.js.resolve.modules 传送门 根据配置不同有以下不同行为:

    • 2.1 如果 item 是数组并且是 pnp 环境则注册 ModulesInHierarchicalDirectoriesPlugin 插件 和 PnpPlugin 插件。
    • 2.2 否则注册 ModulesInHierarchicalDirectoriesPlugin 插件;我们使用 node_modules 就是这个插件处理的。

三、总结

  1. normal-resolve: 普通解析阶段,处理 preferRealative、preferAbsolute
  2. internal: 内部解析阶段,处理 importsFields 选项声明的能力;
  3. raw-module 阶段,处理 resolve.modules、exportsFields 选项声明的能力;