vite 插件打包时记录项目的版本信息让外部访问

605 阅读5分钟

项目开发中遇到了一个问题延伸出了这篇文章

在 WebView 加载 h5 应用时会进行缓存,但 h5 更新后就会出现异常,但是不缓存的话 h5 又会加载的很慢,所以需要在 WebView 初始化时能够检测 H5 应用是否有更新,然后决定是否使用缓存。

所以 h5 需要一个可以外部访问的版本信息,又不想依赖后端,那应该如何实现呢?

将问题拆解后得到:

  1. 项目打包时记录项目的版本、打包日期等信息。
  2. 可以被外部访问

项目打包时记录项目的版本、打包日期是很容易实现的比如使用 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,老话说磨刀不误砍柴工

命名规则

image.png

这个了解一下就好

一个简单的示例

image.png

通过上边的信息我们可以得到

  1. vite 插件是一个函数返回一个插件对象
  2. name 插件名称是必须的
  3. 在提供的钩子函数中实现插件的逻辑

插件钩子函数

就是实现插件逻辑的地方

需求比较简单在一个钩子函数中即可完成,那我们应该把逻辑写在那个钩子函数中呢?

vite 插件约等于 rollup 插件所以先看 rollup 文档

rollup 插件的钩子函数分为两种:构建钩子输出生成钩子

  1. 构建钩子:在构建的各个阶段调用的函数。可以影响构建的运行方式,提供关于构建的信息,或在构建完成后修改构建。
  2. 输出生成钩子:可以提供有关生成的产物的信息并在构建完成后修改构建。

最初选定的是 closeBundle 是输出阶段的最后一个钩子,无论构建有没有错误都会执行一次

但是使用这个钩子会有一个问题,在执行这个钩子函数的时候 vite 已经执行了对 public 目录的拷贝,所以需要手动将更新后的 json 的文件拷贝到 dist 目录中。

看了文档发现 vite 对 public 目录的拷贝是构建完成之后,也就是 buildEnd 钩子函数执行后,那我们就可以使用 buildEnd 及在 buildEnd 之前执行的任意钩子函数中实现逻辑。

我选择在 buildStart 进行实现,原因就是输出的信息会比较清晰,不会打断原有的 vite 打包输出。

  1. buildStart 构建开始时执行
  2. buildEnd 构建结束时执行

情景应用

image.png

这个插件只需要 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')
    },
  }
}

image.png

修改 tsconfig.node.jsoncompilerOptions/lib 添加 DOM

image.png

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

image.png

输出符合预期在开始打包时输出了打印的内容

实现生成 metadata.json 的逻辑

上边说了我们是在 public 目录下创建一个 json 来存储信息,这个文件的名字暂定 metadata.json

我们需要先检查 metadata.json 这个文件是否存在,不存在就新建一个空的 json 文件

这里使用到了 node 相关的 api 需要安装下 pnpm install @types/node -D

image.png

然后读取 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 输出如下:

image.png

这里记录了本次打包的时间和一个 uid ,可以自行扩展里边的字段

验证

起一个本地服务运行下打包后的文件,这里是用 http-server 这个包

进入 dist 目录执行 http-server 会启动一个服务器

image.png

然后请求 http://127.0.0.1:8080/metadata.json

image.png

这样其他需要获取项目版本的地方就可以通过接口请求来获取

到这里我们就学会了如何写一个简单 vite 插件