掘金文章归档 · 第 85 篇 工程化 · Vite 插件

自定义一个 vite 插件,替换更改入口文件 index.html 中的打包结构

transformIndexHtml 给 settings.js 自动追加时间戳,彻底避开浏览器缓存

原文作者:随意_(掘金) | juejin.cn/post/7383889180126969883 | 发布于 2024-06-24 | 阅读 3,977 · 约 5 分钟

0背景

vite 项目部署,我们为了避免多服务器频繁打包部署,抽离了共有变量到 public 中的 settings.js。

settings.js 结构如下:

const defaultSettings = {
  baseApi: "/api", // 接口的base路径 开发为/api 生产可替换/ragapi
  token: "Bearer XXXXXXXXXXXXXX",
  appFrontBase: "http://baidu.com/", // 智能应用前端根路径
  maxFileSize: 900, // 最大的上传文件
  title: '项目名称',
};

代码中可直接使用。现:我们 build 项目到服务器后,更改了 settings.js 中的部分字段。由于缓存的存在,更新完成后 settings.js 请求到的数据仍然为未更新过的(当然,浏览器会一定时间后自动更新静态文件缓存,也可以清空缓存并加载)。但我们想每次部署后,再次刷新浏览器就可以看到最新的 settings.js。

1解决办法

我们自定义一个 vite 插件,实现每次 build 后,将:

<script src="/settings.js"></script>

加一个时间戳,更改为:

<script src="./settings.js?v=1718846778507"></script>

这样,每次 build 部署后,settings 后会有时间戳,每次都不一样,可避免浏览器缓存。

2vite 插件及格式规则

Vite 插件是一种用于扩展 Vite 功能的工具,允许开发者自定义构建配置,以满足特定的开发需求。Vite 插件可以针对开发服务器和生产构建过程进行特定的操作和优化。自定义 Vite 插件通常遵循以下格式:

通过遵循这些格式和约定,开发者可以创建出功能丰富、高度自定义的 Vite 插件,以满足项目开发中的特定需求。

3插件书写

export const vitePluginAddScriptVersion = {
  name: "vite-plugin-add-script-version",
  transformIndexHtml(html) {
    const version = new Date().getTime();
    // 注意这里我们移除了 \s+ 来匹配没有空格的情况,并且使用了 . 来匹配任何字符(除了换行符)
    const regex = /<script\s+src="\.\/settings\.js"([^>]*)>/g;
    // 使用函数作为第二个参数来确保我们只替换没有查询参数的 URL
    const replacedHtml = html.replace(
      regex,
      (_, attrs) => `<script src="./settings.js?v=${version}"${attrs}>`
    );
    return replacedHtml;
  },
};

代码含义解释

部分说明
name插件名称 "vite-plugin-add-script-version"
transformIndexHtml(html)构建过程中转换 HTML 文件内容,接收原始 HTML 字符串
versionnew Date().getTime() 时间戳作为版本号,每次构建唯一
regex全局匹配 src 指向 "./settings.js" 的 script 标签
html.replace函数式替换,给 src 追加 ?v=${version} 并保留原标签其他属性
⚠️ 原文提示:正则里的 ([^>]*) 会匹配 src 后面的任何属性或注释,若 script 标签内有注释或额外属性可能不是预期行为,实际使用请按需调整。

4插件使用

vite-plugin-add-script-version 文件目录和 vite.config.ts 平级。vite.config.ts 引用配置:

import { defineConfig } from 'vite'
import { vitePluginAddScriptVersion } from './vite-plugin-add-script-version'

export default defineConfig({
  plugins: [vitePluginAddScriptVersion]
})

5结果

最终打包后的 index.html 结构如下:

<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="./logo.ico" />
  <script src="./settings.js?v=1718846778507"></script>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script type="module" crossorigin src="./assets/index-DQDZevpy.js"></script>
  <link rel="stylesheet" crossorigin href="./assets/index-CKmqFZ2w.css">
</head>

<body>
  <div id="root" class="h-[100vh]"></div>
</body>

</html>
📌 可见已有版本号,每次部署更新刷新浏览器即可:<script src="./settings.js?v=1718846778507"></script>

6webpack 中如何解决如此问题

在 webpack 的构建过程中,特别是当与一些模板引擎(如 html-webpack-plugin)结合使用时,你会看到这样的语法。这里的 <%= ... %> 是模板引擎的插值语法,用于将某些变量或表达式的值插入到 HTML 文件中。可借助 webpack 的模板语法来实现:

<script src="<%= BASE_URL %>config/config.js?v=<%=new Date().valueOf() %>"></script>