技术文章/工程化

用 Vite 插件解决一个真实的构建产物问题

故事的起点很简单:我们的构建产物需要输出到带版本号的目录,而打包完成后还得手动改一次 HTML 里的引用路径。

先想清楚它在哪个阶段发生

Vite 插件的钩子很多,常见误区是把事情放在 buildEnd 里做——那时候文件已经写完了,你再改就得自己处理文件 IO。正确的位置是 generateBundle:文件已在内存里,但还没落盘。

ts
import type { Plugin } from 'vite'

export function versionedAssets(version: string): Plugin {
  return {
    name: 'wwcong:versioned-assets',
    enforce: 'post',
    generateBundle(_options, bundle) {
      for (const [fileName, chunk] of Object.entries(bundle)) {
        if (chunk.type === 'asset' && fileName.endsWith('.html')) {
          chunk.source = String(chunk.source).replace(
            /(src|href)="\/(_nuxt|assets)\//g,
            `$1="/v${version}/$2/`,
          )
        }
      }
    },
  }
}

代码不多,但有三个细节值得说:

  1. enforce: 'post' 保证它在其他插件的转换之后运行,看到的 HTML 是最终形态。
  2. 直接改 chunk.source,不写文件——交给 Vite 统一落盘。
  3. 版本号从参数传入而不是插件内部读取,这样它就是个纯函数,测试的时候不用 mock 环境变量。

结果

原本每次发布要手动执行的 3 步操作,变成了构建配置里的一行:

ts
plugins: [versionedAssets(pkg.version)]

判断一件事值不值得自动化,我的标准是:它每个月重复超过 5 次,而且每次都能精确描述步骤。 这个改动花了 40 分钟,之后每次发布省 2 分钟,两个月就回本了。

← 上一篇《代码整洁之道》重读:现在我不太同意的一些话

评论

还没有评论,坐个沙发?