使用自定义 loader 记录项目用到的文件,匹配没有用到的资源,然后大胆的删除

115 阅读4分钟

背景:维护的老项目迭代时间较久,里面存放了很多没有引用的资源文件 (包括 image 资源, .vue 文件), 为了大胆的删除这些文件,使用 自定义 loader。 此 loader 是在 webpack 编译阶段 记录使用到的资源文件

仓库地址: 欢迎star

  1. 为什么需要自定义loader, 因为维护的老项目迭代时间较久,里面存放了很多无用的资源,这些资源让我们不太敢随意删除,每次想删除都要去找一下有没有引用, 有的时候这些资源虽然被某一个文件夹入口文件引用了,但是该文件夹却没有被引用,致使我们不知所措,不敢随意删除该资源

  2. 该仓库有两种 loader,record-webapck-loader 适用于普通文件; record-webapck-raw-loader 适用于 二进制文件,比如图片音视频

  3. 两个loader的差异在于能否处理二进制数据,webpack 对于二进制数据处理和文本处理不一致,所以这里拆分为2个 loader 进行处理 (可以匹配 .vue 文件, 可以匹配 .jsx 文件,也可以匹配 图片资源)

  4. 在 webpack 编译阶段 通过自定义 loader 记录特定被编译的文件信息,并且写入到指定文件。有两个参数(writerPath、 clean)。 options.writerPath 文件写入的路径, 此包处理二进制数据 (图片资源)。options.clean 每次启动 webpack 时,是否清空文件, 默认为 true, 每次新启动 webpack 不追加写入 (但是一次编译是追加写入的)

  5. 如何使用: (核心在于匹配文件, 根据 webpack loader 运行规则, 识别到匹配到的文件 写入当前被匹配文件的 path, 记录下来。)

  6. record-webapck-loader 的使用

// webconfig.config.js
 module.exports = {
     ....
     module: {
        rules: [
             {
                test: /.vue$/,
                enforce: "pre",
                exclude: /node_modules/,
                use: [
                    {
                        loader: "record-webapck-loader",
                        options: {
                            writerPath: path.join(__dirname, "./record.log"),
                            clean: true,
                        },
                    }
                ],
            },
        ]
    }
    ...
}         
  1. record-webapck-raw-loader 的使用 (处理图片、媒体文件)
// webconfig.config.js
 module.exports = {
     ....
     module: {
        rules: [
             {
                test: [/.bmp$/, /.gif$/, /.jpe?g$/, /.png$/],
                enforce: "pre",
                exclude: /node_modules/,
                use: [
                    {
                        loader: "record-webapck-loader",
                        options: {
                            writerPath: path.join(__dirname, "./record.log"),
                            clean: true,
                        },
                    }
                ],
            },
        ]
    }
    ...
}         
  1. 效果:(record.log 文件里面记录都是该项目 打包用到 .vue 文件)

image.png

  1. 文件对比: 我们拿到整个项目的所有 .vue 文件,与使用到的 .vue 文件进行对比,即可确定哪些文件没有被用到,可以大胆的删除。

  2. 该 loader 有个缺陷: 即只能确定整个项目所用到的匹配文件,没办法根据某一个路由来确定,当前路由下所依赖的文件,后续会完善这个逻辑 (loader 是参与整个打包过程的, 所以 laoder 可以拿到整个打包过程中所 匹配的文件, 但是假设有一个路由是 /login, 该 loader 没办法确定 login 路由下所有依赖的 匹配文件)

  3. 目前是根据打包全量 扫描,暂不支持 按照 路由级别 去扫描文件 (案例: 某一个具体路由,根据这个具体路由检测 引用文件)

  4. 目前支持的 检测是 文件级别,并不支持 代码级别 (案例:聚合导出的 聚合文件部分被引用 )(目前删除的是 dead file, 并没有 删除 dead code), 后续还需要完善支持 检测 dead code,然后标记 类似 shaking 能力,标记 dead code,然后删除,目前支持的检测到 文件级别的)

  5. 通过上述 loader 已经找到了项目中使用的各种资源文件,那么我们如何做删除呢

// npm i cus-loader-utils -S
const path = require('path')
const {getDiffByDirAndLogFile} = require('cus-loader-utils')

// getAllFileByDir(dirPath, endsWidth) 执行 从指定目录下获取所有文件 <dirPath 表示从哪个目录开始,endsWidth 表示过滤这个目录下的那些后缀名文件>

// getListByLogFile(logPath) 执行 从指定目录下获取 log 文件 并且进行切割为数组 <logPath 表示 log文件 绝对路径>

// removeSameItemByList(liata, liatb) 执行 两个数组的对比 <liata 表示所有数据源的数组,liatb 表示需要被过滤的数组>

// writeResultToFile(writePath, list) 执行 拿到的数组进行写文件 <writePath 表示从写入路径,list 表示 写入的数组>

const { writeResultToFile, removeSameItemByList, getListByLogFile, getAllFileByDir } = getDiffByDirAndLogFile()


let allFile = getAllFileByDir('/Users/xxxx/Documents/indo/yyy/zzz/assets/img/aboutus', '.png')
let logList = getListByLogFile('/Users/xxxx/Documents/indo/yyy/zzz/record.log')
let needWriteList = removeSameItemByList(allFile, logList)
writeResultToFile(path.resolve(__dirname,'abc.js'), needWriteList)