项目开发中遇到了一个问题延伸出了这篇文章
在 WebView 加载 h5 应用时会进行缓存,但 h5 更新后就会出现异常,但是不缓存的话 h5 又会加载的很慢,所以需要在 WebView 初始化时能够检测 H5 应用是否有更新,然后决定是否使用缓存。
所以 h5 需要一个可以外部访问的版本信息,又不想依赖后端,那应该如何实现呢?
将问题拆解后得到:
- 项目打包时记录项目的版本、打包日期等信息。
- 可以被外部访问
项目打包时记录项目的版本、打包日期是很容易实现的比如使用 vite.config.ts 提供了 define 配置:
// 定义全局常量替换,在 vite-env.d 声明后可直接使用
define: {
__BUILD_TIME__: JSON.stringify(dayjs().format('YYYY-MM-DD HH:mm:ss')),
}
但无法实现需求 2 可以被外部访问
需求 2 其实也简单将数据存储到 json 文件中,放在项目 public 目录中就可以通过请求来获取
我们会将更新这个 json 文件的逻辑写成一个 vite 插件,下边来看下官方文档如何写一个 vite 插件
看下 vite 文档
阅读下官方的文档看看怎么写一个 vite,老话说磨刀不误砍柴工
命名规则
这个了解一下就好
一个简单的示例
通过上边的信息我们可以得到
- vite 插件是一个函数返回一个插件对象
name插件名称是必须的- 在提供的钩子函数中实现插件的逻辑
插件钩子函数
就是实现插件逻辑的地方
需求比较简单在一个钩子函数中即可完成,那我们应该把逻辑写在那个钩子函数中呢?
vite 插件约等于 rollup 插件所以先看 rollup 文档
rollup 插件的钩子函数分为两种:构建钩子、输出生成钩子
- 构建钩子:在构建的各个阶段调用的函数。可以影响构建的运行方式,提供关于构建的信息,或在构建完成后修改构建。
- 输出生成钩子:可以提供有关生成的产物的信息并在构建完成后修改构建。
最初选定的是 closeBundle 是输出阶段的最后一个钩子,无论构建有没有错误都会执行一次
但是使用这个钩子会有一个问题,在执行这个钩子函数的时候 vite 已经执行了对 public 目录的拷贝,所以需要手动将更新后的 json 的文件拷贝到 dist 目录中。
看了文档发现 vite 对 public 目录的拷贝是构建完成之后,也就是 buildEnd 钩子函数执行后,那我们就可以使用 buildEnd 及在 buildEnd 之前执行的任意钩子函数中实现逻辑。
我选择在 buildStart 进行实现,原因就是输出的信息会比较清晰,不会打断原有的 vite 打包输出。
buildStart构建开始时执行buildEnd构建结束时执行
情景应用
这个插件只需要 build 时调用所以需要设置 apply: 'build'
到这里实现一个 vite 插件所需的知识储备已经完成了,下边就可以开始动手了
实现插件
在项目中创建 script/vitePluginUpdateMetadata.ts 写入如下内容
import type { Plugin } from 'vite'
export function updateMetadata(): Plugin {
return {
name: 'vite-plugin-update-metadata', // 插件名称
apply: 'build', // 仅在构建时应用
version: '1.0.0', // 插件版本
// 在打包开始时执行
buildStart() {
console.log('开始打包 \r\n')
},
}
}
修改 tsconfig.node.json 在 compilerOptions/lib 添加 DOM
在 vite.config.ts 中引入
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { updateMetadata } from './script/vitePluginUpdateMetadata'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), updateMetadata()],
})
执行下 pnpm run build
输出符合预期在开始打包时输出了打印的内容
实现生成 metadata.json 的逻辑
上边说了我们是在 public 目录下创建一个 json 来存储信息,这个文件的名字暂定 metadata.json
我们需要先检查 metadata.json 这个文件是否存在,不存在就新建一个空的 json 文件
这里使用到了 node 相关的 api 需要安装下 pnpm install @types/node -D
然后读取 metadata.json 修改里边的信息,最后将修改后的信息重新写入。
public 目录下的文件会在打包完后成原封不动的复制到产物目录即 dist 中,但需要注意插件钩子的执行时机。
完整代码如下
import type { Plugin } from 'vite'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import crypto from 'crypto'
export function updateMetadata(): Plugin {
return {
name: 'vite-plugin-update-metadata', // 插件名称
apply: 'build', // 仅在构建时应用
version: '1.0.0', // 插件版本
// 在打包完成后执行
buildStart() {
// 获取 metadata.json 文件路径
const metadataPath = path.resolve(process.cwd(), 'public/metadata.json')
// 检查 Metadata.json 是否存在
if (!fs.existsSync(metadataPath)) {
// 不存在则创建一个空的 metadata.json 文件
fs.writeFileSync(metadataPath, JSON.stringify({}, null, 2), 'utf-8')
}
// 读取并解析 JSON 文件
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'))
// 更新 buildTime 字段为当前时间
metadata.buildTime = new Date().toLocaleString()
// 更新 uid 字段
metadata.uid = crypto.randomUUID()
// 将更新后的 JSON 写回文件
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8')
console.table(metadata)
console.log('metadata.json 数据已经更新完成!\r\n')
},
}
}
执行 pnpm run build 输出如下:
这里记录了本次打包的时间和一个 uid ,可以自行扩展里边的字段
验证
起一个本地服务运行下打包后的文件,这里是用 http-server 这个包
进入 dist 目录执行 http-server 会启动一个服务器
然后请求 http://127.0.0.1:8080/metadata.json
这样其他需要获取项目版本的地方就可以通过接口请求来获取
到这里我们就学会了如何写一个简单 vite 插件