赞
踩
webpack插件相信很多前端开发都使用过,但是实际开发中需要自定义一个webpack插件的情况也有很多,试想着这样一个开发场景: 我们想知道在一个项目中都有哪些文件或者模块引用了某一个组件,难道要通过搜索一个一个文件去代码里找吗?有没有更简便的方法实现它?
本文通过实现一个webpack自定义插件,来输出在一个项目中所有引用某一个模块的功能。(基于webpack v5.75.0/webpack-cli 5.0.1,本文的主要目的是梳理自定义插件的思路和方法,建立webpack插件开发使用体系,建议在开发中不要直接copy该组件使用,而是根据实际的业务和对应版本webpack的api自行开发)
如图在项目中,src底下有3个js文件
index.js
- import { age } from './tapable';
- import foo from './test';
-
- foo(); // foo如果执行了将会在test.txt中打印两个index的路径,即被引用了两次
- console.log(age)
tapable.js
- export const age = '5656';
-
- import foo from './test';
test.js
- export default function foo() {
- console.log('test组件')
- }
可以看到,其中test.js文件被index.js和tapable.js组件引用。
webpack.config.js的plugins配置如下:
这里jsFilePath就是我需要查询index.js被哪些模块引用;
addText:表示是否需要生成一个文件来记录这些输出地址
outPath:表示文件的输出地址和文件名
- plugins: [
- new FindDepsPlugin({
- jsFilePath: path.resolve(__dirname, 'src/test.js'), // 需要查询的文件路径
- addText: true, // 是否生成.txt文件
- outPath: './dist_my/public/test.txt' // 生产的文件地址和路径
- })
- ],
其中FindDepsPlugin就是我们要自定义的插件,jsFilePath,addText,outPath参数定义已经在上述代码中标明。
接下来定义FindDepsPlugin插件,设计思路如下:
1.在插件的 apply 方法中,通过 compilation.hooks.optimizeChunkAssets 钩子获取到所有已优化过的 chunk 对象。
2.遍历每个 chunk 的模块依赖关系(可以使用 chunk.getModules() 方法),找到所有的模块对象。
3.找出这些模块的引用部分dependencies,通过compilation.moduleGraph.getModule(dep).resource获取以来的源路径,判断模块所有的依赖列表中是否含有jsFilePath,如果有就把该模块收集起来
4.记录下这些模块的源文件路径(可以使用 module.resource 属性获取)。
5.在 compiler.hooks.done 钩子中输出这些记录下来的源文件路径。
find-deps-Plugin.js
- const fs = require('fs')
- const path = require('path')
-
- class FindDepsPlugin {
- constructor(options = {}) {
- this.jsFilePath = options.jsFilePath || ''
- this.outPath = options.outPath || './test.txt';
- this.addText = options.addText || false;
- this.deps = []
- }
-
- apply(compiler) {
- compiler.hooks.compilation.tap('FindDepsPlugin', compilation => {
- compilation.hooks.optimizeChunkAssets.tapAsync(
- 'FindDepsPlugin',
- (chunks, callback) => {
- chunks.forEach(chunk => {
- const modules = chunk.getModules();
- const isHasPath = modules.find(item => item.resource && item.resource === path.resolve(this.jsFilePath));
- // 如果输入的jsFilePath参数是有效的
- if (isHasPath) {
- // 用来收集依赖模块中含有jsFilePath的module
- const list = [];
- modules.forEach(module => {
- (module.dependencies || []).forEach(dep => {
- const depResource = compilation.moduleGraph.getModule(dep) && compilation.moduleGraph.getModule(dep).resource;
- if (depResource === path.resolve(this.jsFilePath)) {
- // 判断模块所有的依赖列表中是否含有jsFilePath,如果有就把该模块收集起来
- list.push(module);
- }
- });
- });
- const depModules = list.map(module => module.resource);
- this.deps.push(...depModules)
- }
- })
- callback()
- }
- )
- })
-
- compiler.hooks.done.tap('FindDepsPlugin', () => {
- console.log(`"${this.jsFilePath}" 组件被以下文件所引用:`)
- // 为了解决循环引用的问题,需要对this.deps进行去重处理
- this.deps = [...new Set(this.deps)];
- this.deps.forEach(dep => {
- console.log(dep)
- });
- if (this.addText) {
- fs.writeFile(this.outPath, this.deps.join('\n'), 'utf8', err => {
- if (err) {
- console.log('err', err);
- return
- }
- console.log('写入文件成功')
- });
- }
- })
- }
- }
-
- module.exports = FindDepsPlugin;
验证下输出:
可以看到在终端中输出了index和tapable的路径
而 outPath参数对应的./dist_my/public文件夹中多了一个test.txt文件,输出的也是index和tapable的路径
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。