1:前言
关于打包优化之前已经讲过很多优秀处理方案,今天我们来谈论一下如何将“瘦身”更近一步。废话不多说!我们知道在前端工具链完成最后一步块写入的时候,其实我们还是可以做一些更加细致的操作。比如:可以将打包好的文件加入一些你要注入的的代码或者想要移植的文件,同时你也可以对你的打包结果做一些安全检查或者更直接的去除一些“赘肉”。那么现在我们要做的是去除赘肉,完成“瘦身”这个动作。我个人认为最好的方案是在构建流中完成这个操作,可以使用VITE
插件的形式在构建钩子中完成这个执行流且相对简单、直接、高效。
2:流程方案
3:代码流
3.1. 代码
首先创建一个插件文件,写入以下代码(代码不多,注意需要自己简单配置一下!)
const fs = require('fs') // 引入 Node.js 文件系统模块
const path = require('path') // 引入 Node.js 路径模块
const distPath = path.resolve(__dirname, '../../../dist') // 定义构建输出目录的绝对路径
// 定义一个白名单,包含不被删除的文件或目录
const whiteList = new Set([
'__init__.py' // 示例目录,可以根据需要修改
])
/**
* 获取目录下所有文件
* @param {string} dir - 目录路径
* @param {Array} files_ - 文件数组,递归时传递
* @returns {Array} - 返回所有文件路径数组
*/
function getFiles(dir, files_ = []) {
const files = fs.readdirSync(dir) // 读取目录下的所有文件和子目录
for (const file of files) {
const name = path.join(dir, file) // 获取文件的完整路径
if (fs.statSync(name).isDirectory()) {
getFiles(name, files_) // 如果是目录,递归读取
} else {
files_.push(name) // 如果是文件,加入文件数组
}
}
console.log('files_:', files_)
return files_
}
/**
* 检查文件内容中是否有引用
* @param {string} fileContent - 文件内容
* @param {Array} allFiles - 所有文件路径数组
* @returns {Set} - 返回引用的文件集合
*/
function scanForReferences(fileContent, allFiles) {
const referencedFiles = new Set() // 创建一个集合,存储引用的文件
for (const file of allFiles) {
const fileName = path.basename(file) // 获取文件名
if (fileContent.includes(fileName)) {
referencedFiles.add(fileName) // 如果文件内容中引用了该文件,加入集合
}
}
return referencedFiles
}
/**
* 主函数
*/
function cleanUnreferencedFiles() {
const allFiles = getFiles(distPath) // 获取构建输出目录下的所有文件
const referencedFiles = new Set(whiteList) // 初始化为白名单中的文件
// 收集所有引用的文件
for (const file of allFiles) {
if (/\.(html|css|js)$/.test(file)) {
const content = fs.readFileSync(file, 'utf-8') // 读取文件内容
const refs = scanForReferences(content, allFiles) // 扫描文件内容中的引用
refs.forEach((ref) => referencedFiles.add(ref)) // 将引用的文件加入集合
}
}
// 过滤出未被引用且不在白名单中的文件
const filesToRemove = allFiles.filter((file) => {
const relativePath = path.relative(distPath, file) // 获取文件的相对路径
const baseName = path.basename(file, '.gz') // 获取基础文件名(去掉 .gz 扩展名)
return !referencedFiles.has(baseName) && !isInWhiteList(relativePath) // 如果文件未被引用且不在白名单中,标记为删除
})
// 删除未被引用的文件
for (const file of filesToRemove) {
fs.unlinkSync(file) // 删除文件
console.log(`Removed unreferenced file: ${file}`) // 输出删除日志
}
}
/**
* 检查文件是否在白名单中
* @param {string} filePath - 文件路径
* @returns {boolean} - 返回文件是否在白名单中的结果
*/
function isInWhiteList(filePath) {
for (const item of whiteList) {
if (filePath.startsWith(item)) {
// 如果文件路径以白名单中的某项开头,返回 true
return true
}
}
return false // 否则返回 false
}
// cleanUnreferencedFiles()
// 导出执行主函数
module.exports = function squeakyCleanPlugin() {
return {
name: 'vite-plugin-squeaky-clean', // 插件名称
apply: 'build', // 指定插件在构建阶段应用
writeBundle() {
cleanUnreferencedFiles()
}
}
}
3.2. 嵌入构建流
4:执行
核对一下是不是真的成功了
5:最后
我们顺利的在执行流中顺利的完成了我们的瘦身动作,并且在执行transform之前。同时我们还可以看到这个“瘦身”具有通用性,稍加改动就可以完美的移植到所有的vite架构上。总的来说这是一次非常精准的瘦身,不多不少,正是我们想要的结果。