阿里云主机折上折
  • 微信号
Current Site:Index > The basic structure and lifecycle of a plugin

The basic structure and lifecycle of a plugin

Author:Chuan Chen 阅读数:18823人阅读 分类: 构建工具

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:

  1. config → Modify initial configuration
  2. configResolved → Read final configuration
  3. configureServer → Development server configuration
  4. buildStart → Build starts
  5. resolveId → Resolve module paths
  6. load → Load module content
  7. transform → Transform module content
  8. buildEnd → Build ends
  9. closeBundle → 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

  1. Context Preservation: Use this to access plugin context
{
  configureServer(server) {
    this.server = server // Store reference
  }
}
  1. Hot Update Handling: Implement via handleHotUpdate
{
  handleHotUpdate(ctx) {
    if (ctx.file.endsWith('.data')) {
      ctx.server.ws.send({ type: 'full-reload' })
      return []
    }
  }
}
  1. Virtual File System: Combine resolveId and load
{
  resolveId(id) {
    if (id.startsWith('virtual:')) {
      return '\0' + id
    }
  },
  load(id) {
    if (id.startsWith('\0virtual:')) {
      return `export default ${JSON.stringify({ magic: 42 })}`
    }
  }
}
  1. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.