插件
example
插件名称和发布的 npm 包名建议统一,建议格式为 vite-plugin-xxx
// myPlugin.js
// 使用方式
// vite.config.ts
import { myVitePlugin } from "./myVitePlugin"
export function myVitePlugin(options) {
console.log(options)
return {
name: "vite-plugin-xxx",
load(id) {
// 在钩子逻辑中可以通过闭包访问外部的 options 传参
},
}
}
export default {
plugins: [
myVitePlugin({
/* 给插件传参 */
}),
],
}// myPlugin.js
// 使用方式
// vite.config.ts
import { myVitePlugin } from "./myVitePlugin"
export function myVitePlugin(options) {
console.log(options)
return {
name: "vite-plugin-xxx",
load(id) {
// 在钩子逻辑中可以通过闭包访问外部的 options 传参
},
}
}
export default {
plugins: [
myVitePlugin({
/* 给插件传参 */
}),
],
}hook
通用 hook
Vite 开发阶段会模拟 Rollup 的行为,其中 Vite 会调用一系列与 Rollup 兼容的钩子,主要分为三个阶段:
- 服务器启动阶段:options 和 buildStart 钩子会在服务启动时被调用
- 请求响应阶段:当浏览器发起请求时,Vite 内部依次调用 resolveId 、load 和 transform 钩子
- 服务器关闭阶段:Vite 会依次执行 buildEnd 和 closeBundle 钩子
除了以上钩子,其他 Rollup 插件钩子 ( 如 moduleParsed 、renderChunk ) 均不会在 Vite 开发阶段调用。
而在生产环境下,由于 Vite 2.x 直接使用 Rollup ,Vite 插件中所有 Rollup 的插件钩子都会生效。
独有 hook
这些 hook 只会在 Vite 内部调用,放到 Rollup 中会被直接忽略。
config
给配置加料。
Vite 在读取完配置文件 ( 即 vite.config.ts ) 后,会拿到用户导出的配置对象,然后执行 config 钩子。
在这个钩子里,可以对配置文件导出的对象进行自定义操作。
推荐在 config 钩子中返回一个配置对象,这个配置对象会和 Vite 已有的配置进行深度的合并。但也可以通过钩子的入参拿到 config 对象进行自定义修改。
// 返回部分配置(推荐)
const editConfigPlugin = () => ({
name: "vite-plugin-modify-config",
config: () => ({
alias: {
react: require.resolve("react"),
},
}),
})
// 自定义修改
const mutateConfigPlugin = () => ({
name: "mutate-config",
// command 为 `serve`(开发环境) 或者 `build`(生产环境)
config(config, { command }) {
// 生产环境中修改 root 参数
if (command === "build") {
config.root = __dirname
}
},
})// 返回部分配置(推荐)
const editConfigPlugin = () => ({
name: "vite-plugin-modify-config",
config: () => ({
alias: {
react: require.resolve("react"),
},
}),
})
// 自定义修改
const mutateConfigPlugin = () => ({
name: "mutate-config",
// command 为 `serve`(开发环境) 或者 `build`(生产环境)
config(config, { command }) {
// 生产环境中修改 root 参数
if (command === "build") {
config.root = __dirname
}
},
})configResolved
记录最终配置。
Vite 在解析完配置后会调用 configResolved 钩子,这个钩子一般用来记录最终的配置信息,而不建议再修改配置。
const exmaplePlugin = () => {
let config
return {
name: "read-config",
configResolved(resolvedConfig) {
// 记录最终配置
config = resolvedConfig
},
// 在其他钩子中可以访问到配置
transform(code, id) {
console.log(config)
},
}
}const exmaplePlugin = () => {
let config
return {
name: "read-config",
configResolved(resolvedConfig) {
// 记录最终配置
config = resolvedConfig
},
// 在其他钩子中可以访问到配置
transform(code, id) {
console.log(config)
},
}
}configureServer
获取 Dev Server 实例。
这个钩子仅在开发阶段会被调用,用于扩展 Vite 的 Dev Server ,一般用于增加自定义 server 中间件。
const myPlugin = () => ({
name: "configure-server",
configureServer(server) {
// 姿势 1: 在 Vite 内置中间件之前执行
server.middlewares.use((req, res, next) => {
// 自定义请求处理逻辑
})
// 姿势 2: 在 Vite 内置中间件之后执行
return () => {
server.middlewares.use((req, res, next) => {
// 自定义请求处理逻辑
})
}
},
})const myPlugin = () => ({
name: "configure-server",
configureServer(server) {
// 姿势 1: 在 Vite 内置中间件之前执行
server.middlewares.use((req, res, next) => {
// 自定义请求处理逻辑
})
// 姿势 2: 在 Vite 内置中间件之后执行
return () => {
server.middlewares.use((req, res, next) => {
// 自定义请求处理逻辑
})
}
},
})transformIndexHtml
转换 HTML 内容。
这个钩子用来灵活控制 HTML 的内容,可以拿到原始的 html 内容后进行任意的转换。
const htmlPlugin = () => {
return {
name: "html-transform",
transformIndexHtml(html) {
return html.replace(/<title>(.*?)<\/title>/, "<title>换了个标题</title>")
},
}
}
// 也可以返回如下的对象结构,一般用于添加某些标签
const htmlPlugin = () => {
return {
name: "html-transform",
transformIndexHtml(html) {
return {
html,
// 注入标签
tags: [
{
// 放到 body 末尾,可取值还有`head`|`head-prepend`|`body-prepend`,顾名思义
injectTo: "body",
// 标签属性定义
attrs: { type: "module", src: "./index.ts" },
// 标签名
tag: "script",
},
],
}
},
}
}const htmlPlugin = () => {
return {
name: "html-transform",
transformIndexHtml(html) {
return html.replace(/<title>(.*?)<\/title>/, "<title>换了个标题</title>")
},
}
}
// 也可以返回如下的对象结构,一般用于添加某些标签
const htmlPlugin = () => {
return {
name: "html-transform",
transformIndexHtml(html) {
return {
html,
// 注入标签
tags: [
{
// 放到 body 末尾,可取值还有`head`|`head-prepend`|`body-prepend`,顾名思义
injectTo: "body",
// 标签属性定义
attrs: { type: "module", src: "./index.ts" },
// 标签名
tag: "script",
},
],
}
},
}
}handleHotUpdate
热更新处理。
这个钩子会在 Vite 服务端处理热更新时被调用,可以在这个钩子中拿到热更新相关的上下文信息,进行热更模块的过滤,或者进行自定义的热更处理。
const handleHmrPlugin = () => {
return {
async handleHotUpdate(ctx) {
// 需要热更的文件
console.log(ctx.file)
// 需要热更的模块,如一个 Vue 单文件会涉及多个模块
console.log(ctx.modules)
// 时间戳
console.log(ctx.timestamp)
// Vite Dev Server 实例
console.log(ctx.server)
// 读取最新的文件内容
console.log(await read())
// 自行处理 HMR 事件
ctx.server.ws.send({
type: "custom",
event: "special-update",
data: { a: 1 },
})
return []
},
}
}
// 前端代码中加入
if (import.meta.hot) {
import.meta.hot.on("special-update", (data) => {
// 执行自定义更新
// { a: 1 }
console.log(data)
window.location.reload()
})
}const handleHmrPlugin = () => {
return {
async handleHotUpdate(ctx) {
// 需要热更的文件
console.log(ctx.file)
// 需要热更的模块,如一个 Vue 单文件会涉及多个模块
console.log(ctx.modules)
// 时间戳
console.log(ctx.timestamp)
// Vite Dev Server 实例
console.log(ctx.server)
// 读取最新的文件内容
console.log(await read())
// 自行处理 HMR 事件
ctx.server.ws.send({
type: "custom",
event: "special-update",
data: { a: 1 },
})
return []
},
}
}
// 前端代码中加入
if (import.meta.hot) {
import.meta.hot.on("special-update", (data) => {
// 执行自定义更新
// { a: 1 }
console.log(data)
window.location.reload()
})
}总结
- config :进一步修改配置
- configResolved :记录最终的配置信息
- configureServer :获取 Vite Dev Server 实例,添加中间件
- transformIndexHtml :转换 HTML 内容
- handleHotUpdate :进行热更新模块的过滤,或者进行自定义的热更新处理
hook 执行顺序
// test-hooks-plugin.ts
// 注: 请求响应阶段的钩子
// 如 resolveId, load, transform, transformIndexHtml在下文介绍
// 以下为服务启动和关闭的钩子
export default function testHookPlugin() {
return {
name: "test-hooks-plugin",
// Vite 独有钩子
config(config) {
console.log("config")
},
// Vite 独有钩子
configResolved(resolvedCofnig) {
console.log("configResolved")
},
// 通用钩子
options(opts) {
console.log("options")
return opts
},
// Vite 独有钩子
configureServer(server) {
console.log("configureServer")
setTimeout(() => {
// 手动退出进程
process.kill(process.pid, "SIGTERM")
}, 3000)
},
// 通用钩子
buildStart() {
console.log("buildStart")
},
// 通用钩子
buildEnd() {
console.log("buildEnd")
},
// 通用钩子
closeBundle() {
console.log("closeBundle")
},
}
}// test-hooks-plugin.ts
// 注: 请求响应阶段的钩子
// 如 resolveId, load, transform, transformIndexHtml在下文介绍
// 以下为服务启动和关闭的钩子
export default function testHookPlugin() {
return {
name: "test-hooks-plugin",
// Vite 独有钩子
config(config) {
console.log("config")
},
// Vite 独有钩子
configResolved(resolvedCofnig) {
console.log("configResolved")
},
// 通用钩子
options(opts) {
console.log("options")
return opts
},
// Vite 独有钩子
configureServer(server) {
console.log("configureServer")
setTimeout(() => {
// 手动退出进程
process.kill(process.pid, "SIGTERM")
}, 3000)
},
// 通用钩子
buildStart() {
console.log("buildStart")
},
// 通用钩子
buildEnd() {
console.log("buildEnd")
},
// 通用钩子
closeBundle() {
console.log("closeBundle")
},
}
}- 服务启动阶段:config、configResolved、options、configureServer、buildStart
- 请求响应阶段:如果是 html 文件,仅执行 transformIndexHtml ;对非 html 文件,则依次执行 resolveId、load 和 transform
- 热更新阶段:执行 handleHotUpdate
- 服务关闭阶段:执行 buildEnd 和 closeBundle
应用位置
应用情景和应用顺序。
默认情况下 Vite 插件同时被用于开发环境和生产环境,可以通过 apply 属性决定应用场景
{
// 'serve' 表示仅用于开发环境,'build'表示仅用于生产环境
apply: "serve"
}
// 也可以配置成函数
{
apply(config, { command }) {
// 只用于非 SSR 情况下的生产环境构建
return command === 'build' && !config.build.ssr
}
}{
// 'serve' 表示仅用于开发环境,'build'表示仅用于生产环境
apply: "serve"
}
// 也可以配置成函数
{
apply(config, { command }) {
// 只用于非 SSR 情况下的生产环境构建
return command === 'build' && !config.build.ssr
}
}可以通过 enforce 属性指定插件的执行顺序
{
// 默认为`normal`,可取值还有`pre`和`post`
enforce: "pre"
}{
// 默认为`normal`,可取值还有`pre`和`post`
enforce: "pre"
}执行顺序
- Alias ( 路径别名 ) 相关的插件
- 带有
enforce: "pre"的用户插件 - Vite 核心插件
- 没有
enforce的用户插件 ( 普通插件 ) - Vite 生产环境构建用的插件
- 带有
enforce: "post"的用户插件 - Vite 后置构建插件 ( 如压缩插件 )
Ayingotts's notes