跳到主要内容

插件系统

插件概述

Vite 插件扩展了构建流程的功能。Vite 的插件系统基于 Rollup 插件接口,并添加了 Vite 特定的配置钩子。

插件类型

  1. 官方插件:由 Vite 团队维护
  2. 社区插件:由社区开发者贡献
  3. 自定义插件:根据项目需求自行开发

官方插件列表

插件用途安装命令
@vitejs/plugin-vueVue 3 单文件组件支持npm i -D @vitejs/plugin-vue
@vitejs/plugin-vue-jsxVue 3 JSX 支持npm i -D @vitejs/plugin-vue-jsx
@vitejs/plugin-reactReact Fast Refreshnpm i -D @vitejs/plugin-react
@vitejs/plugin-react-swcReact SWC 编译npm i -D @vitejs/plugin-react-swc
@vitejs/plugin-legacy旧浏览器支持npm i -D @vitejs/plugin-legacy
@vitejs/plugin-basic-ssl开发服务器 HTTPSnpm i -D @vitejs/plugin-basic-ssl

使用插件

基本用法

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
plugins: [
vue(),
],
})

插件配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import legacy from '@vitejs/plugin-legacy'

export default defineConfig({
plugins: [
// 使用默认配置
vue(),

// 使用自定义配置
legacy({
targets: ['defaults', 'not IE 11'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
}),
],
})

条件使用插件

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig(({ mode }) => ({
plugins: [
vue(),
// 只在生产构建时启用分析插件
mode === 'production' && visualizer({
open: true,
gzipSize: true,
}),
].filter(Boolean), // 过滤掉 false 值
}))

常用社区插件

路径别名解析

npm install -D vite-tsconfig-paths
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
plugins: [tsconfigPaths()],
})

自动导入

npm install -D unplugin-auto-import
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
plugins: [
AutoImport({
imports: ['vue', 'vue-router'],
dts: true, // 生成类型声明文件
}),
],
})

组件自动导入

npm install -D unplugin-vue-components
import { defineConfig } from 'vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
plugins: [
Components({
resolvers: [ElementPlusResolver()],
}),
],
})

SVG 处理

npm install -D vite-plugin-svgr
import { defineConfig } from 'vite'
import svgr from 'vite-plugin-svgr'

export default defineConfig({
plugins: [
svgr({
// 将 SVG 作为 React 组件导入
exportAsDefault: true,
}),
],
})

使用:

import { ReactComponent as Logo } from './logo.svg'

图片压缩

npm install -D vite-plugin-imagemin
import { defineConfig } from 'vite'
import viteImagemin from 'vite-plugin-imagemin'

export default defineConfig({
plugins: [
viteImagemin({
gifsicle: { optimizationLevel: 7 },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.8, 0.9] },
svgo: {
plugins: [
{ name: 'removeViewBox' },
{ name: 'removeEmptyAttrs', active: false },
],
},
}),
],
})

PWA 支持

npm install -D vite-plugin-pwa
import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate',
manifest: {
name: 'My App',
short_name: 'MyApp',
theme_color: '#ffffff',
icons: [
{
src: '/icon-192x192.png',
sizes: '192x192',
type: 'image/png',
},
],
},
}),
],
})

检查器(TypeScript/ESLint)

npm install -D vite-plugin-checker
import { defineConfig } from 'vite'
import checker from 'vite-plugin-checker'

export default defineConfig({
plugins: [
checker({
typescript: true,
eslint: {
lintCommand: 'eslint "./src/**/*.{ts,tsx}"',
},
}),
],
})

创建自定义插件

插件基本结构

// my-plugin.js
export default function myPlugin(options = {}) {
return {
// 插件名称(用于警告和错误信息)
name: 'my-plugin',

// 应用顺序('pre' | 'post' | undefined)
enforce: 'pre',

// 仅在构建时应用
apply: 'build',

// 配置钩子...
}
}

常用钩子

解析钩子

export default function myPlugin() {
return {
name: 'my-plugin',

// 解析 ID 时调用
resolveId(source, importer) {
if (source === 'virtual-module') {
return source // 返回虚拟模块 ID
}
},

// 加载模块时调用
load(id) {
if (id === 'virtual-module') {
return 'export default "Hello from virtual module!"'
}
},
}
}

转换钩子

export default function myPlugin() {
return {
name: 'my-plugin',

// 转换代码时调用
transform(code, id) {
// 只处理 .js 文件
if (!id.endsWith('.js')) return null

// 替换代码中的特定内容
const transformed = code.replace(
/__VERSION__/g,
JSON.stringify(process.env.npm_package_version)
)

return {
code: transformed,
map: null, // 可选:source map
}
},
}
}

构建钩子

export default function myPlugin() {
return {
name: 'my-plugin',

// 构建开始时调用
buildStart() {
console.log('构建开始!')
},

// 构建结束时调用
buildEnd() {
console.log('构建结束!')
},

// 输出文件生成时调用
generateBundle(options, bundle) {
// 遍历所有生成的文件
for (const [fileName, chunk] of Object.entries(bundle)) {
console.log(`生成文件: ${fileName}`)
}
},

// 写入文件后调用
writeBundle(options, bundle) {
console.log('文件写入完成!')
},
}
}

Vite 特定钩子

export default function myPlugin() {
return {
name: 'my-plugin',

// 配置 Vite 服务器
configureServer(server) {
// 添加自定义中间件
server.middlewares.use('/api', (req, res, next) => {
// 处理 API 请求
})
},

// 转换 HTML
transformIndexHtml(html) {
return html.replace(
/<title>(.*?)<\/title>/,
`<title>My App - $1</title>`
)
},

// 处理热更新
handleHotUpdate({ file, server, modules }) {
console.log(`文件更新: ${file}`)
// 可以返回自定义的模块列表
return modules
},
}
}

完整插件示例

虚拟模块插件

// virtual-module-plugin.js
export default function virtualModulePlugin() {
const virtualModuleId = 'virtual:config'
const resolvedVirtualModuleId = '\0' + virtualModuleId

return {
name: 'virtual-module-plugin',

resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId
}
},

load(id) {
if (id === resolvedVirtualModuleId) {
return `
export const appName = 'My App'
export const version = '1.0.0'
export const apiUrl = '${process.env.VITE_API_URL}'
`
}
},
}
}

使用:

import { appName, version } from 'virtual:config'

console.log(appName, version)

环境变量注入插件

// env-replace-plugin.js
export default function envReplacePlugin(options = {}) {
const { prefix = 'VITE_' } = options

return {
name: 'env-replace-plugin',

transform(code, id) {
// 只处理 JS/TS 文件
if (!/\.(js|ts|jsx|tsx)$/.test(id)) return null

// 查找所有 __ENV_*__ 模式
const envRegex = /__ENV_(\w+)__/g
let match
let transformed = code

while ((match = envRegex.exec(code)) !== null) {
const varName = match[1]
const envValue = process.env[`${prefix}${varName}`]

if (envValue !== undefined) {
transformed = transformed.replace(
match[0],
JSON.stringify(envValue)
)
}
}

return { code: transformed }
},
}
}

文件复制插件

// copy-files-plugin.js
import fs from 'fs'
import path from 'path'

export default function copyFilesPlugin(options = {}) {
const { from, to } = options

return {
name: 'copy-files-plugin',

writeBundle() {
const srcDir = path.resolve(from)
const destDir = path.resolve(to)

if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true })
}

const files = fs.readdirSync(srcDir)

for (const file of files) {
const srcFile = path.join(srcDir, file)
const destFile = path.join(destDir, file)

fs.copyFileSync(srcFile, destFile)
console.log(`Copied: ${file}`)
}
},
}
}

使用:

// vite.config.js
import copyFilesPlugin from './copy-files-plugin'

export default defineConfig({
plugins: [
copyFilesPlugin({
from: './public/locales',
to: './dist/locales',
}),
],
})

插件排序

enforce 选项

控制插件应用顺序:

export default defineConfig({
plugins: [
{
name: 'pre-plugin',
enforce: 'pre', // 在其他插件之前运行
transform(code) {
// ...
},
},
{
name: 'normal-plugin',
// 默认顺序
transform(code) {
// ...
},
},
{
name: 'post-plugin',
enforce: 'post', // 在其他插件之后运行
transform(code) {
// ...
},
},
],
})

apply 选项

控制插件在何时应用:

export default defineConfig({
plugins: [
{
name: 'build-only',
apply: 'build', // 仅在构建时应用
},
{
name: 'serve-only',
apply: 'serve', // 仅在开发服务器时应用
},
{
name: 'conditional',
apply(config, { command }) {
// 自定义条件
return command === 'build' && config.build?.minify
},
},
],
})

插件最佳实践

1. 命名规范

插件名称应该清晰描述其功能:

// ✅ 好的命名
{ name: 'vite-plugin-svg-icons' }
{ name: 'vite-plugin-compression' }

// ❌ 不好的命名
{ name: 'my-plugin' }
{ name: 'plugin' }

2. 错误处理

在插件中妥善处理错误:

export default function myPlugin() {
return {
name: 'my-plugin',

transform(code, id) {
try {
// 转换逻辑
return { code: transformed }
} catch (error) {
this.error(`转换失败: ${id}`, error)
}
},
}
}

3. 性能优化

避免不必要的处理:

export default function myPlugin() {
return {
name: 'my-plugin',

transform(code, id) {
// 快速过滤不相关的文件
if (!id.endsWith('.special')) return null

// 检查是否需要转换
if (!code.includes('SPECIAL_TAG')) return null

// 执行转换
return { code: transform(code) }
},
}
}

4. Source Map 支持

如果转换代码,提供 source map:

import { createSourceMap } from 'some-sourcemap-lib'

export default function myPlugin() {
return {
name: 'my-plugin',

transform(code, id) {
const result = transformWithSourceMap(code, id)

return {
code: result.code,
map: result.map, // 提供 source map
}
},
}
}

小结

本章我们学习了 Vite 的插件系统:

  1. 官方插件:Vue、React、Legacy 等官方维护的插件
  2. 社区插件:路径别名、自动导入、PWA 等常用插件
  3. 自定义插件
    • 插件基本结构和钩子
    • 解析、加载、转换钩子
    • Vite 特定的服务器和 HTML 钩子
  4. 插件排序:使用 enforceapply 控制插件行为
  5. 最佳实践:命名、错误处理、性能优化

插件是扩展 Vite 功能的核心机制,掌握插件开发可以让你根据项目需求定制构建流程。