The basic structure and lifecycle of a plugin
Plugins are the core extension mechanism in the Vite ecosystem, integrating into the build process through hook functions. Understanding the structure and lifecycle of plugins is crucial for customized development, as it determines when code executes and how it affects the build results.
Basic Structure of a Plugin
A standard Vite plugin needs to export an object containing specific properties. The most basic form is as follows:
// my-plugin.js
export default function myPlugin(options) {
return {
name: 'vite-plugin-my', // Required unique identifier
config(config) { /*...*/ }, // Configuration hook
transform(code, id) { /*...*/ } // Transformation hook
}
}
Key structural elements include:
- name: Plugin name, recommended to prefix with
vite-plugin-
- enforce: Adjust execution order (pre|post)
- apply: Specify the plugin's application scenario (development/production)
- config: Entry point for modifying Vite configuration
- transform: File content transformation handler
Complex plugins may include multiple lifecycle hooks:
export default {
name: 'vite-plugin-multi-hook',
// Configuration phase
config(config, env) {
console.log('Current mode:', env.mode)
},
// Build phase
buildStart() {
console.log('Build started')
},
// File processing
load(id) {
if (id.endsWith('.custom')) {
return 'export default "Hacked!"'
}
}
}
Detailed Lifecycle Phases
Configuration Resolution Phase
The config
hook executes before resolving user configuration and supports synchronous/asynchronous operations:
{
async config(config, env) {
if (env.mode === 'development') {
return {
server: {
port: 3001
}
}
}
}
}
configResolved
triggers after the configuration is finalized, suitable for reading the final configuration:
{
configResolved(resolvedConfig) {
console.log('Final port number:', resolvedConfig.server.port)
}
}
Module Loading Phase
resolveId
controls module path resolution:
{
resolveId(source, importer) {
if (source === 'magic') {
return '\0virtual:magic' // Add virtual module marker
}
}
}
load
handles module content loading:
{
load(id) {
if (id === '\0virtual:magic') {
return 'export const spell = "Abracadabra"'
}
}
}
Transformation Phase
transform
transforms source code:
{
transform(code, id) {
if (id.endsWith('.vue')) {
return code.replace('<template>', '<template lang="pug">')
}
}
}
transformIndexHtml
specifically processes the HTML entry:
{
transformIndexHtml(html) {
return html.replace(
'<head>',
'<head><meta name="injected" content="true">'
)
}
}
Build Event Phase
buildStart
and buildEnd
mark build boundaries:
{
buildStart() {
this.startTime = Date.now()
},
buildEnd() {
console.log(`Build duration: ${Date.now() - this.startTime}ms`)
}
}
closeBundle
performs cleanup operations after bundling:
{
async closeBundle() {
await fs.remove('temp/')
}
}
Hook Execution Order
Example of a complete lifecycle flow:
config
→ Modify initial configurationconfigResolved
→ Read final configurationconfigureServer
→ Development server configurationbuildStart
→ Build startsresolveId
→ Resolve module pathsload
→ Load module contenttransform
→ Transform module contentbuildEnd
→ Build endscloseBundle
→ Resource cleanup
Adjust execution position with enforce
:
{
name: 'early-plugin',
enforce: 'pre', // Execute before normal plugins
transform(code) { /*...*/ }
}
{
name: 'late-plugin',
enforce: 'post', // Execute after normal plugins
transform(code) { /*...*/ }
}
Practical Examples
Implementing a plugin to automatically inject environment variables:
// vite-plugin-inject-env.js
export default function injectEnv() {
return {
name: 'vite-plugin-inject-env',
transform(code, id) {
if (!id.includes('node_modules')) {
return code.replace(
/import\.meta\.env/g,
'JSON.parse(process.env.VITE_ENV)'
)
}
},
generateBundle(options, bundle) {
for (const file in bundle) {
if (file.endsWith('.js')) {
bundle[file].code = `const process = { env: { VITE_ENV: ${JSON.stringify(process.env)} } };\n${bundle[file].code}`
}
}
}
}
}
CSS preprocessing plugin example:
// vite-plugin-less-vars.js
import less from 'less'
export default function lessVars(vars) {
return {
name: 'vite-plugin-less-vars',
async transform(code, id) {
if (!id.endsWith('.less')) return
const result = await less.render(code, {
globalVars: vars,
javascriptEnabled: true
})
return {
code: result.css,
map: result.map
}
}
}
}
Plugin Development Considerations
- Context Preservation: Use
this
to access plugin context
{
configureServer(server) {
this.server = server // Store reference
}
}
- Hot Update Handling: Implement via
handleHotUpdate
{
handleHotUpdate(ctx) {
if (ctx.file.endsWith('.data')) {
ctx.server.ws.send({ type: 'full-reload' })
return []
}
}
}
- Virtual File System: Combine
resolveId
andload
{
resolveId(id) {
if (id.startsWith('virtual:')) {
return '\0' + id
}
},
load(id) {
if (id.startsWith('\0virtual:')) {
return `export default ${JSON.stringify({ magic: 42 })}`
}
}
}
- Performance Optimization: Use
moduleParsed
to track dependencies
{
moduleParsed(moduleInfo) {
if (moduleInfo.id.includes('utils')) {
console.log('Detected utility module dependencies:', moduleInfo.importedIds)
}
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:配置智能提示与类型支持
下一篇:官方核心插件功能介绍