We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
全文 1000 字,阅读时长约 10 min,请放心食用。作者近期会一直 focus 在 webpack 领域,对此感兴趣的同学记得点赞关注。
本文讲解 webpack 的 module.issuer 属性,内容涵盖该属性的作用、运行原理,并结合 webpack 实例讲解应用场景。
module.issuer
在 webpack 实现上,文件资源使用 Module 类管理,所有关于资源的操作、转译、合并、关系都在 module 实现。而 module.issuer 属性用于记录资源的引用者,例如对于下面的资源依赖:
Module
module
index 引用了 a/b 两个文件,webpack 构建时会用三个 module 对象分别对应三个文件,同时在 a/b 模块中通过 issuer 属性指向 index 模块:
index
a/b
issuer
module['a.js'].issuer = module['index.js']
module['b.js'].issuer = module['index.js']
通过 issuer 属性,模块可以反向查找到引用者。
Stats 是 webpack 内置的对象,用于收集构建过程信息,比如耗时、模块依赖关系、错误信息、报警信息等,我们运行 webpack 命令输出的命令行信息就是由 Stats 类提供的:
Stats
如果编译过程发生错误,Stats 会通过 module.issuer 属性逐级往上查找出完整调用堆栈:
class Stats { constructor(compilation) { // ... } toJson(options, forToString) { const formatError = (e) => { // ... if (showModuleTrace && e.origin) { text += `\n @ ${this.formatFilePath( e.origin.readableIdentifier(requestShortener) )}`; // ... while (current.issuer) { current = current.issuer; text += `\n @ ${current.readableIdentifier(requestShortener)}`; } } return text; }; } }
最终输出下图最后两行错误堆栈:
issuer 属性定义在 webpack/lib/Module.js 的 construct 函数,但 webpack 中使用较少,难以追踪,这里取了个巧,用 Object.defineProperty 拦截 issuer 属性,定位出哪里获取 / 修改了这个属性:
webpack/lib/Module.js
construct
Object.defineProperty
// webpack/lib/Module.js class Module extends DependenciesBlock { constructor(type, context = null) { // ... // 注释掉原本的定义 // this.issuer = null; // 用 Object.defineProperty 拦截 this._issuer=null; Object.defineProperty(this, "issuer", { get: () => { debugger; return this._issuer; }, set: (value) => { debugger; this._issuer = value; }, }); // ... } }
之后,使用 ndb 断点追踪 issuer 的使用情况,可以看到只有在 compilation 对象的 addModuleDependencies 函数中触发 set 函数:
ndb
compilation
addModuleDependencies
set
class Compilation { addModuleDependencies( module, dependencies, bail, cacheGroup, recursive, callback ) { // ... if (addModuleResult.issuer) { if (currentProfile) { dependentModule.profile = currentProfile; } // 触发更改 dependentModule.issuer = module; } // ... } }
addModuleDependencies 函数的作用是为已有 module 添加依赖声明,例如对于上面的例子:
在 compilation 解析 (解析过程可参考: [万字总结] 一文吃透 Webpack 核心原理) 出 index.js 内容的 AST 后,遍历 require/import 语句解读当前模块引用了那些资源,解析到任意依赖后就会调用 addModuleDependencies 记录依赖关系,从 addModuleDependencies 源码看在依赖被创建为 module 时,会同步修改新模块的 issuer ,记录引用者的信息。
index.js
require/import
基于 module.issuer ,我们可以从特定 module 出发反向遍历依赖关系链,为此我写了个示例插件:
function RevertTracePlugin(options) { } RevertTracePlugin.prototype.apply = function(compiler) { // compilation 被创建出来后触发 compiler.hooks.thisCompilation.tap("RevertTracePlugin", function(compilation) { // 构建模块前触发 compilation.hooks.buildModule.tap("RevertTracePlugin", (module) => { const stack = []; let current = module; // 向上遍历,找出所有引用者 while (current.issuer) { stack.push(current.issuer.rawRequest); current = current.issuer; } if (stack.length > 0) { console.group(`资源 ${module.rawRequest} 引用链: `); console.log(stack.join("\n")); console.groupEnd(); console.log(); } }); }); };
上述插件用到两个钩子:
compiler.hooks.thisCompilation
compilation.hooks.buildModule
关于 webpack 钩子的更多内容,可查阅往前文章:
在 buildModule 钩子内部通过 while 循环不断向上遍历,最终可追溯到完整引用链条,例如对于下图的文件依赖关系:
buildModule
while
入口 index.js 文件引用了 a.js,a.js 文件引用了 b.js,上述插件运行效果:
a.js
b.js
module.issuer 属性在 webpack 中使用的比较少,因为大多数时候模块间的依赖关系都可以通过 dependency graph 相关的属性正向获取,下一期我们就来聊聊 dependency graph 相关内容,感兴趣的同学欢迎点赞关注。 https://zhuanlan.zhihu.com/p/368391369
The text was updated successfully, but these errors were encountered:
No branches or pull requests
本文讲解 webpack 的
module.issuer
属性,内容涵盖该属性的作用、运行原理,并结合 webpack 实例讲解应用场景。module.issuer 是什么
在 webpack 实现上,文件资源使用
Module
类管理,所有关于资源的操作、转译、合并、关系都在module
实现。而module.issuer
属性用于记录资源的引用者,例如对于下面的资源依赖:index
引用了a/b
两个文件,webpack 构建时会用三个module
对象分别对应三个文件,同时在a/b
模块中通过issuer
属性指向index
模块:module['a.js'].issuer = module['index.js']
module['b.js'].issuer = module['index.js']
通过
issuer
属性,模块可以反向查找到引用者。实例: Stats 类
Stats
是 webpack 内置的对象,用于收集构建过程信息,比如耗时、模块依赖关系、错误信息、报警信息等,我们运行 webpack 命令输出的命令行信息就是由Stats
类提供的:如果编译过程发生错误,
Stats
会通过module.issuer
属性逐级往上查找出完整调用堆栈:最终输出下图最后两行错误堆栈:
源码
issuer 属性定义在
webpack/lib/Module.js
的construct
函数,但 webpack 中使用较少,难以追踪,这里取了个巧,用Object.defineProperty
拦截issuer
属性,定位出哪里获取 / 修改了这个属性:之后,使用
ndb
断点追踪issuer
的使用情况,可以看到只有在compilation
对象的addModuleDependencies
函数中触发set
函数:addModuleDependencies
函数的作用是为已有module
添加依赖声明,例如对于上面的例子:在
compilation
解析 (解析过程可参考: [万字总结] 一文吃透 Webpack 核心原理) 出index.js
内容的 AST 后,遍历require/import
语句解读当前模块引用了那些资源,解析到任意依赖后就会调用addModuleDependencies
记录依赖关系,从addModuleDependencies
源码看在依赖被创建为module
时,会同步修改新模块的issuer
,记录引用者的信息。示例: 追溯模块引用关系
基于
module.issuer
,我们可以从特定module
出发反向遍历依赖关系链,为此我写了个示例插件:上述插件用到两个钩子:
compiler.hooks.thisCompilation
:webpack 启动编译,创建出compilation
对象时触发,在示例场景中作为中间步骤,用于获取创建出的compilation
对象compilation.hooks.buildModule
:执行模块构建之前触发,此时module
对象所有原信息都初始化完毕,可以正常获取到issuer
属性在
buildModule
钩子内部通过while
循环不断向上遍历,最终可追溯到完整引用链条,例如对于下图的文件依赖关系:入口
index.js
文件引用了a.js
,a.js
文件引用了b.js
,上述插件运行效果:总结
module.issuer
属性在 webpack 中使用的比较少,因为大多数时候模块间的依赖关系都可以通过 dependency graph 相关的属性正向获取,下一期我们就来聊聊 dependency graph 相关内容,感兴趣的同学欢迎点赞关注。https://zhuanlan.zhihu.com/p/368391369
The text was updated successfully, but these errors were encountered: