插件原理
通过自定义webpack插件,利用执行完成编译的封存阶段后,产生的产物module.fileDependencies,生成依赖的文件组。通过读文件的方式,将待扫描的文件组和有依赖关系的文件进行对比。最终暴露出项目中,不存在依赖关系的文件,并可配置将其全部删除。
代码实现
1、自定义webpack插件,配置options。遍历stats.compilation.fileDependencies,存储依赖文件。
const fs = require('fs');
const path = require('path');class UnDependencClearPlugin {constructor(options = {}) {this.options = options;this.entry = options.entry || 'src'; // 入口this.include = options.include || ''; // 包含哪些文件'.vue|.js'this.exclude = options.exclude || ''; // 排除哪些文件夹 ['src/assets', 'src/views']this.isDelete = options.isDelete || false; // 是否主动删除文件this.originFile = []; // node读取的源文件目录 处理过include及exclude 后的数据 最全的数据this.dependenciesFile = []; // webpack依赖数据 处理过include及exclude 后的数据 依赖数据this.noUseFile = []; // 可删除的数据this.init(); // 初始化}apply(compiler) {compiler.hooks.done.tapAsync('UnDependencClearPlugin', (stats, cb) => {// 获取依赖let curFile = [];stats.compilation.fileDependencies.forEach((item) => {curFile.push(item);});// 排除node_modules和entrycurFile = curFile.filter((item) => {if (item.indexOf('node_modules') == -1 &&item.indexOf(this.resolve(this.entry)) > -1) {return item;}});// 处理includeconst includeFile = this.includeHandle(curFile);// 处理excludeconst excludeFile = this.excludeHandle(includeFile);this.dependenciesFile = excludeFile;// 从 originFile 及 dependenciesFile分析出未被使用的数据this.originFile.forEach((item) => {if (this.dependenciesFile.findIndex((el) => el == item) == -1) {this.noUseFile.push(item);}});// 处理资源 写入文件this.writeDirPathHandle();cb();});}// 初始化init() {}// 处理规则includeHandle(list) {return filterFile;}// 处理规则excludeHandle(list) {return filterFile;}// 写入文件writeDirPathHandle() {}// 删除文件deleteFileHandle() {}
}
module.exports = UnDependencClearPlugin;
2、通过配置的include文件类型,使用includeHandle方法进行文件类型筛选,
// 处理规则includeHandle(list) {if (!this.include) {return list;}// 指定类型的文件const includeArr = this.include.split('|');const filterFile = list.filter((item) => {const index = includeArr.findIndex((el) => item.indexOf(el) > -1);if (index > -1) {return item;}});return filterFile;}
3、配置过滤规则
// 处理规则excludeHandle(list) {if (!this.exclude) {return list;}// 过滤指定文件夹const excludeList = [];this.exclude.forEach((item) => {excludeList.push(this.resolve(item));});const filterFile = list.filter((item) => {const index = excludeList.findIndex((el) => item.indexOf(el) > -1);if (index == -1) {return item;}});return filterFile;}
4、将产物写入文件,用户可清晰看见被扫描的所有文件、存在依赖的文件、无用文件
// 写入文件writeDirPathHandle() {let content = `所有文件-length[${this.originFile.length}]、依赖文件-length[${this.dependenciesFile.length}]、无用文件-length[${this.noUseFile.length}]`;content += `\r\n###所有文件-length[${this.originFile.length}]###\r\n${this.originFile.join('\n')}\r\n`;content += `\r\n###依赖文件-length[${this.dependenciesFile.length}]###\r\n${this.dependenciesFile.join('\n')}\r\n`;content += `\r\n###无用文件-length[${this.noUseFile.length}]####\r\n${this.noUseFile.join('\n')}\r\n`;fs.writeFile('dependency.txt', content, (err) => {if (err) {console.error(err);return;}// 判断是否执行删除if (this.isDelete) {this.deleteFileHandle();}});}
5、使用时,配置开关isDelete,开启后可自动删除无用文件
// 删除文件deleteFileHandle() {this.noUseFile.forEach((item) => {fs.unlink(item, (err) => {if (err) throw err;});});}
使用方法
1、在项目中添加undependencClearPlugin.js文件
2、在webpack.config.js文件中配置plugin
const undependencClearPlugin = require('./undependencClearPlugin');isEnvDevelopment &&new undependencClearPlugin({entry: '/src',include: '.js|.vue|.jpg',exclude: ['./node_modules'],isDelete: false,}),
3、对于想主动清理代码这个场景,只需在菜单中删除或注释菜单的引用文件
//路由
component: () => import('@/xxx/xxx/xxx.vue')
//或者
require(['./xxx/xxx.vue'], resolve)
// 或者引入的文件import xxx from './xxx/xxx/xxx'