阿里云主机折上折
  • 微信号
Current Site:Index > Custom Plugin Development Guide

Custom Plugin Development Guide

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

The plugin system of Vite.js is based on the Rollup plugin mechanism, allowing developers to intervene in the build process through hook functions. Custom plugins can extend build capabilities, such as transforming code, handling static assets, or modifying module resolution logic. Below, we will expand step by step from plugin structure, commonly used hooks to practical examples.

Basic Plugin Structure

A Vite plugin needs to export a function or object containing a name and core hook functions. The simplest form of a plugin is as follows:

// vite-plugin-example.js
export default function myPlugin() {
  return {
    name: 'vite-plugin-example',
    // Plugin hooks will be defined here
    transform(code, id) {
      if (id.endsWith('.custom')) {
        return { code: code.replace(/foo/g, 'bar') }
      }
    }
  }
}

The plugin configuration is introduced via vite.config.js:

import myPlugin from './vite-plugin-example'

export default {
  plugins: [myPlugin()]
}

Core Hooks Explained

config Hook

Executed before resolving the Vite configuration, allowing modification of the final configuration. Receives the original configuration and an env object:

config(config, { mode }) {
  if (mode === 'development') {
    config.server.port = 3001
  }
}

transformIndexHtml

A hook specifically for processing HTML entry files:

transformIndexHtml(html) {
  return html.replace(
    '<title>Default Title</title>',
    '<title>Customized Title</title>'
  )
}

resolveId

Custom module resolution logic. The following example redirects virtual:module to a real path:

resolveId(source) {
  if (source === 'virtual:module') {
    return '\0' + source // Add virtual module marker
  }
}

load

Load virtual module content:

load(id) {
  if (id === '\0virtual:module') {
    return 'export const msg = "From virtual module"'
  }
}

Virtual Module Example

Creating a virtual module that provides environment variables:

// vite-plugin-env.js
export default function envPlugin(envVars) {
  const virtualModuleId = 'virtual:env'

  return {
    name: 'vite-plugin-env',
    resolveId(id) {
      if (id === virtualModuleId) {
        return '\0' + virtualModuleId
      }
    },
    load(id) {
      if (id === '\0' + virtualModuleId) {
        return `export default ${JSON.stringify(envVars)}`
      }
    }
  }
}

Usage:

// vite.config.js
import envPlugin from './vite-plugin-env'

export default {
  plugins: [
    envPlugin({
      API_URL: 'https://api.example.com'
    })
  ]
}

// Reference in components
import env from 'virtual:env'
console.log(env.API_URL)

Hot Module Reloading (HMR) Handling

Implement custom HMR logic via handleHotUpdate:

handleHotUpdate({ file, server }) {
  if (file.endsWith('.data')) {
    server.ws.send({
      type: 'custom',
      event: 'data-update',
      data: { changed: file }
    })
  }
}

Client-side event listening:

if (import.meta.hot) {
  import.meta.hot.on('data-update', (data) => {
    console.log('File changed:', data.changed)
  })
}

Static Asset Transformation

Process specific asset types via transform. The following example converts .txt files into JS modules:

transform(code, id) {
  if (id.endsWith('.txt')) {
    return {
      code: `export default ${JSON.stringify(code)}`,
      map: null
    }
  }
}

Plugin Configuration Options

It is recommended to use a factory function to receive user configurations:

// vite-plugin-options.js
export default function(options = {}) {
  return {
    name: 'vite-plugin-options',
    config() {
      console.log('Received options:', options)
    }
  }
}

// Configuration example
plugins: [
  require('./vite-plugin-options')({
    featureFlags: ['new-header', 'dark-mode']
  })
]

Performance Optimization Tips

  1. Use enforce: 'pre' to make the plugin execute before core plugins:
return {
  name: 'vite-plugin-early',
  enforce: 'pre'
}
  1. Control the plugin's runtime environment via apply:
apply: 'build' // or 'serve'
  1. Cache transformation results:
transform(code, id) {
  if (hasCache(id)) {
    return cache.get(id)
  }
  // ...processing logic
}

Debugging Tips

Insert debugging breakpoints in the plugin:

configResolved(config) {
  debugger
  // Execution will pause here
}

Access development server information via the server instance:

configureServer(server) {
  server.httpServer?.once('listening', () => {
    console.log('Server port:', server.config.server.port)
  })
}

Complex Example: Markdown Transformation

A complete plugin to convert .md files into Vue components:

// vite-plugin-md.js
import { compile } from 'markdown-to-jsx-compiler'

export default function mdPlugin() {
  return {
    name: 'vite-plugin-md',
    transform(code, id) {
      if (!id.endsWith('.md')) return

      const jsx = compile(code)
      return {
        code: `
          <script setup>
          const content = ${JSON.stringify(jsx)}
          </script>
          <template>
            <div v-html="content" />
          </template>
        `,
        map: null
      }
    }
  }
}

Plugin Interaction

Data sharing between multiple plugins can be achieved via the meta object:

// Plugin A writes data
transform(code, id) {
  this.meta.sharedData = { version: '1.0' }
}

// Plugin B reads data
configResolved(config) {
  console.log('Shared data:', config.plugins
    .find(p => p.name === 'plugin-a')
    ?.meta.sharedData)
}

Build Phase Extension

Example of modifying Rollup output hooks:

generateBundle(options, bundle) {
  for (const [fileName, chunk] of Object.entries(bundle)) {
    if (fileName.endsWith('.js')) {
      chunk.code = `/* Build time: ${new Date().toISOString()} */\n${chunk.code}`
    }
  }
}

Error Handling Standards

Recommended way to throw errors:

transform(code, id) {
  try {
    // Transformation logic
  } catch (err) {
    this.error('Transformation failed', {
      line: err.line,
      column: err.column
    })
  }
}

Type Hint Support

Add TypeScript type definitions for the plugin:

// types/vite-plugin-example.d.ts
import { Plugin } from 'vite'

declare interface PluginOptions {
  prefix?: string
}

export declare function myPlugin(options?: PluginOptions): Plugin

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱: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 ☕.