介绍
Vite插件是在Vite构建过程中运行的函数,本质上就是一个功能函数,导出后在Vite plugin中进行调用即可。同时,Vite在整个构建过程中为插件提供了很多钩子,我们可以通过这些钩子来进一步控制我们的插件功能。有了上述准备工作,实现一个Vite插件将十分简单!
| 钩子名称 | 作用 |
|---|---|
config | 修改 Vite 配置。可以同步或异步返回一个对象,或者返回一个 Promise |
configResolved | 在 Vite 配置解析后调用,可以在这里进一步修改配置 |
configureServer | 配置开发服务器 |
transformIndexHtml | 转换 index.html 内容 |
resolveId | 自定义模块路径解析 |
load | 自定义加载模块内容 |
transform | 转换模块内容 |
buildStart | 构建开始时调用 |
buildEnd | 构建结束时调用 |
closeBundle | 打包完成时调用 |
handleHotUpdate | 处理热更新 |
开始
创建一个js文件
在根目录下,创建一个js文件,这里我将其命名为visualizer.js,在文件内默认导出一个函数,这个函数就是插件的核心逻辑函数。
这里我主要收集了文件大小、打包耗时等变量。
变量定义
let startTime // 打包开始时间
let jsSize = 0, cssSize = 0, imageSize = 0, fontSize = 0, htmlSize = 0 // 打包文件大小
let transformCount = 0 // 文件解析数量
const fileSizeStack = [] // 文件体积信息栈
变量定义好后,函数体内返回一个对象,这个对象包括插件名称和钩子函数,我们所有逻辑都在各个钩子函数内进行实现。
统计打包时间
统计打包时间很简单,只需要在buildStart中记录一下当前时间,这个是打包开始时的时间点,然后在closeBundle中再一次获取当前时间,这是打包结束的时间点,二者相减,再除以1000(毫秒转秒),就能得到整体的打包耗时
buildStart() {
startTime = Date.now()
},
closeBundle() {
const endTime = Date.now()
console.log(`🚀打包耗时:${(endTime - startTime) / 1000}s`);
},
统计打包后的文件大小
打包文件大小统计应该写在generateBundle钩子中,这个钩子在bundle生成后触发,该函数接收两个参数:outputOptions输出产物配置项,outputBundle输出bundle信息。
代码实现
generateBundle(outputOptions, outputBundle) {
for (const [bundleId, bundle] of Object.entries(outputBundle)) {
const extname = bundle.fileName.slice(bundle.fileName.lastIndexOf('.'))
const size = bundle?.code?.length || bundle?.source?.length
switch (extname) {
case '.js':
jsSize += size
break
case '.css':
cssSize += size
break
case ".jpg":
case ".jpeg":
case ".png":
case ".gif":
case ".svg":
imageSize += size
break
case '.html':
htmlSize += size
break
case ".woff":
case ".woff2":
case ".ttf":
case ".otf":
fontSize += size;
break;
default:
break;
}
}
},
通过switch语句,将不同类型的文件的size做分类收集处理,最后汇总进行展示。
解析过程中,如果我想拿到每个文件的大小和当前已解析文件数量该如何做呢,其实可以通过transform钩子函数进行获取,该函数接收code和id两个参数,分别代表解析的文件的源代码和文件的完整路径(文件指纹)。
这里做文件类型的匹配,node_modules/下的文件忽略。
transform(code, id) {
transformCount++
const reg = /\.(vue|ts|js|jsx|tsx|css|scss||sass|styl|less)$/gi
const nodeModuleReg = /node_modules/gi;
if (!nodeModuleReg.test(id) && reg.test(id)) {
fileSizeStack.push({
id,
size: code.length
})
console.log(id + ': ' + (code.length / 1024).toFixed(2) + 'KB');
console.log('已解析文件数:', transformCount);
}
}
结果展示
在loseBundle中展示最终的所有结果,其中,在上一步transform中,我们构建了一个fileSizeStack,用于存放文件的路径和对应的源码体积,用于统计最终的文件体积进行从大到小排序。不过当前fileSizeStack需要进行加工才能完成最终展示。
实现排序工具函数
function sort10Size(fileSizeStack) {
return (
fileSizeStack.sort((a, b) => b.size - a.size).slice(0, 10)
)
}
实现格式转换工具函数
function formatFileSize(fileSizeStack) {
return (
fileSizeStack.map(item => `${item.id}: ${(item.size / 1024).toFixed(2)}KB\n`)
)
}
通过以上两个函数串行调用就可以实现上述需求目标了。
console.log(`📦文件体积排名前十:\n${formatFileSize(sort10Size(fileSizeStack))}`);
不过我还希望最终打包结果在控制台输出的文本可以高亮成其他颜色,以和过程日志进行区分。
引入kolorist工具库,该库主要用于控制node控制台的日志样式。
import { green } from 'kolorist'
// 修改console.log的颜色
export const successColor = function (s) {
return green(s)
}
现在,closeBundle就可以完整输出以上整理的打包信息了。
closeBundle() {
const endTime = Date.now()
console.log(successColor(`📦文件体积排名前十:\n${formatFileSize(sort10Size(fileSizeStack))}`));
console.log(successColor(`📦打包后js文件大小:${(jsSize / 1024).toFixed(2)}KB, css文件大小:${(cssSize / 1024).toFixed(2)}KB, 图片文件大小:${(imageSize / 1024).toFixed(2)}KB, 字体文件大小:${(fontSize / 1024).toFixed(2)}KB, html文件大小:${(htmlSize / 1024).toFixed(2)}KB`))
console.log(successColor(`🚀打包耗时:${(endTime - startTime) / 1000}s`));
},
使用
在vite.config.ts中引入visualizer文件,并在plugins数组中调用默认导出的函数。
import visualizer from './visualizer';
export default defineConfig({
// ......
plugins: [visualizer()]
})
运行 npm run build 执行打包命令,等待打包进程完成。
看到控制台已经可以正常输出打包文件的一些信息了,之后每次进行本地build都可以进行打包文件信息的展示,可以用于分析打包性能和bundle大小,进而方便进行针对性的打包性能优化。