Vite 插件如何用 transformIndexHtml 转换 HTML?
【免费下载链接】viteNext generation frontend tooling. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vi/vite
如果你的需求是"在构建或开发过程中程序化地改写 HTML 入口文件"——比如替换<title>、按构建状态向<head>注入<script>或<link>标签——就需要在 Vite 插件里实现transformIndexHtml钩子。这是 Vite 独有的插件钩子(Rollup 会忽略它),会在 dev 和 build 两种场景下自动作用于 HTML 入口文件,如index.html。钩子是async、sequential调用的,属于 per-environment 钩子。
什么时候需要写这个钩子
Vite 内置了简单的 HTML 常量替换:任何import.meta.env中的属性都可以用%CONST_NAME%语法写进 HTML(见 env-and-mode.md):
<h1>Vite is running in %MODE%</h1> <p>Using data from %VITE_API_URL%</p>但 Vite 对条件分支这类复杂替换是刻意保持不干预的。当简单替换不够用时,文档给出的扩展方式就是:使用社区现成的转换类插件,或自己写一个实现transformIndexHtml钩子 的自定义插件。
两个前置结论先说明:
- 插件可以直接内联在
vite.config.js里,不需要为此新建一个包(见 using-plugins.md)。 - 如果你使用的框架自己接管了入口文件(文档以 SvelteKit 为例),这个钩子不会被调用,写之前先确认项目是否属于这种情况。
最短主路径:返回替换后的 HTML 字符串
钩子接收当前 HTML 字符串和一个转换上下文,最简单的写法是直接返回替换后的字符串(以下示例来自 api-plugin.md 的官方示例):
const htmlPlugin = () => { return { name: 'html-transform', transformIndexHtml(html) { return html.replace( /<title>(.*?)<\/title>/, `<title>Title replaced!</title>`, ) }, } }把它注册到vite.config.js的plugins数组即可:
import { defineConfig } from 'vite' export default defineConfig({ plugins: [htmlPlugin()], })plugins中的 falsy 项会被忽略,所以可以用条件表达式方便地启用/停用插件。
三种返回值:改字符串、注入标签、两者同时做
钩子可以返回以下三者之一:
- 转换后的 HTML 字符串——上面的标题替换例子;
- 标签描述对象数组(
{ tag, attrs, children })——注入到现有 HTML 中,每个标签可单独指定注入位置,默认注入到<head>的开头; - 同时包含两者的对象
{ html, tags }。
标签描述符的完整类型如下(来自文档的 Full Hook Signature):
type IndexHtmlTransformHook = ( html: string, ctx: { path: string filename: string server?: ViteDevServer bundle?: import('rolldown').OutputBundle chunk?: import('rolldown').OutputChunk originalUrl?: string }, ) => IndexHtmlTransformResult | void | Promise<IndexHtmlTransformResult | void> type IndexHtmlTransformResult = | string | HtmlTagDescriptor[] | { html: string tags: HtmlTagDescriptor[] } interface HtmlTagDescriptor { tag: string /** * attribute values will be escaped automatically if needed */ attrs?: Record<string, string | boolean> children?: string | HtmlTagDescriptor[] /** * default: 'head-prepend' */ injectTo?: 'head' | 'body' | 'head-prepend' | 'body-prepend' }其中attrs的值在需要时会被自动转义。第二个参数ctx在 dev 时暴露ViteDevServer实例,在 build 时暴露输出 bundle——也就是说同一个钩子函数可以按运行阶段读取不同的上下文信息(完整类型声明见 api-plugin.md)。
用order控制钩子的执行时机
钩子也可以写成对象形式:IndexHtmlTransformHook | { order?: 'pre' | 'post', handler: IndexHtmlTransformHook }。
- 默认
order为undefined,钩子在 HTML 已被转换之后应用; order: 'pre'在 Vite 处理 HTML 之前应用,文档明确给出的用途是:注入需要经过 Vite 插件流水线的 script;order: 'post'在所有order为 undefined 的钩子之后应用。
const myPlugin = () => ({ name: 'inject-pre-script', transformIndexHtml: { order: 'pre', handler(html) { return [{ tag: 'script', attrs: { src: '/entry.js' } }] }, }, })注意这和插件级的enforce: 'pre' | 'post'(调整整个插件相对 Vite 核心插件的位置)是两回事,见 api-plugin.md 的 Plugin Ordering 一节。
如何验证转换生效
钩子在 dev 和 build 下都会被调用(Vite 插件的默认行为是两种场景都执行),所以验证分两条:
- dev:启动开发服务器后,直接访问入口 HTML 页面,检查渲染出的
<title>、注入的标签是否按预期出现;order: 'pre'注入的 script 应当走 Vite 的模块处理流程。 - build:运行构建命令,检查产物中对应 HTML 文件里的替换/注入结果,确认钩子在构建阶段同样生效。
调试插件时,文档建议把vite-plugin-inspect加入项目,安装后访问localhost:5173/__inspect/可以检查模块和转换栈的中间状态。
边界与限制
- 框架接管入口文件时钩子不触发:文档特别警告,若框架对入口文件有自定义处理(例如 SvelteKit),
transformIndexHtml不会被调用。 - 钩子作用域是 per-environment,跨环境行为以 api-environment-plugins.md 的说明为准。
%CONST_NAME%不存在的变量不会被替换(保留原样),这与 JS 中import.meta.env.X得到undefined的行为不同;复杂逻辑不要指望它,交给本钩子。- 另有一个同名程序化 API:自定义 SSR 中间件里可以用
vite.transformIndexHtml(url, template)手动对模板应用 Vite 内置 HTML 转换和插件 HTML 转换(见 ssr.md 与 api-javascript.md)。那是服务端渲染中间件场景的用法,与插件钩子本身是不同入口,按需选择。
【免费下载链接】viteNext generation frontend tooling. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vi/vite
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考