返回首页
🎨 前端 / Web

Vite 插件开发完整指南:从入门到发布

Vite 6 插件开发完整指南。本文从 Rollup 钩子开始,演示 5 个实战插件的开发与发布流程。

Vite · 插件 · 构建工具 · 前端
📰

今日技术简讯

📰 技术简讯 · 2026-06-20

今日聚合 6 条热门技术内容(周六)。

🤖 AI / LLM

1. OpenAI Realtime API GA

🎨 前端 / Web

2. Vite 插件开发完整指南

3. Qwik 2.0 发布

  • 链接https://qwik.dev/blog/qwik-2
  • 来源:Qwik
  • 摘要:Resumability 框架 Qwik 2.0,性能大幅提升,与 React 互操作更顺滑。

⚙️ 后端 / 架构

4. ClickHouse 推出 Kafka Engine 优化

🚀 独立开发 / OPC

5. Outseta 推出全套订阅工具

6. Vercel 推出 v0 设计师版

  • 链接https://v0.dev/designer
  • 来源:Vercel
  • 摘要:v0 Designer 让设计师用 AI 生成生产级代码。

数据来源:HN / Reddit / 各厂博客 采集时间:2026-06-20 09:00 (UTC+8)

📝

今日深度文

Vite 插件开发完整指南:从入门到发布

一句话结论:Vite 插件 = Rollup 插件 + Vite 特有钩子。学习 Rollup 插件机制是入门关键。

背景

Vite 的插件生态是其核心优势之一。理解 Vite 插件开发,能让你:

  • 解决项目特有的构建问题
  • 复用团队的最佳实践
  • 为开源社区贡献

核心概念

Vite 插件 = Rollup 插件 + 扩展

// 一个最简单的 Vite 插件
import type { Plugin } from 'vite';

export function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    // 钩子函数
    transform(code, id) {
      // 处理代码
      return code;
    },
  };
}

常用钩子

const hooks = {
  // 配置相关
  options: '解析用户配置',
  configResolved: 'Vite 配置完全解析后',
  
  // 构建相关(Rollup 钩子)
  transform: '转换单个模块',
  load: '加载自定义模块',
  resolveId: '自定义模块解析',
  generateBundle: '生成 bundle 时',
  
  // Vite 特有
  configureServer: '配置开发服务器',
  handleHotUpdate: '处理 HMR',
  transformIndexHtml: '转换 index.html',
};

实战 1:自动 import 插件

// vite-plugin-auto-import.ts
import type { Plugin } from 'vite';
import { readFile } from 'fs/promises';
import { resolve } from 'path';

interface Options {
  dirs?: string[];
  prefix?: string;
}

export function autoImport(options: Options = {}): Plugin {
  const { dirs = ['src/components', 'src/utils'], prefix = '' } = options;
  
  return {
    name: 'vite-plugin-auto-import',
    
    async configResolved() {
      // 构建时扫描所有文件
      this.autoImports = new Map<string, string>();
      
      for (const dir of dirs) {
        const files = await glob(`${dir}/**/*.{ts,tsx,js,jsx}`);
        for (const file of files) {
          const name = path.basename(file, path.extname(file));
          this.autoImports.set(name, file);
        }
      }
    },
    
    transform(code, id) {
      // 检测到 import { Button } from 'auto' 时
      if (id.endsWith('main.ts') || id.endsWith('App.tsx')) {
        const imports: string[] = [];
        for (const [name, file] of this.autoImports!) {
          if (code.includes(`<${name}`)) {
            imports.push(`import ${name} from '${file}';`);
          }
        }
        if (imports.length) {
          return imports.join('\n') + '\n' + code;
        }
      }
    },
  };
}

实战 2:Markdown 自动编译插件

// vite-plugin-md.ts
import { Plugin } from 'vite';
import { marked } from 'marked';
import { readFile } from 'fs/promises';

export function markdown(): Plugin {
  return {
    name: 'vite-plugin-markdown',
    
    enforce: 'pre',  // 在其他插件前执行
    
    async load(id) {
      if (id.endsWith('.md')) {
        const code = await readFile(id, 'utf-8');
        const html = marked.parse(code);
        
        // 转成 ES Module
        return `
          export default ${JSON.stringify(html)};
        `;
      }
    },
  };
}

使用:

import content from './article.md';
document.getElementById('app')!.innerHTML = content;

实战 3:HMR 增强插件

// vite-plugin-reload-on-save.ts
import { Plugin } from 'vite';

export function reloadOnSave(extensions: string[] = ['.json', '.env']): Plugin {
  return {
    name: 'reload-on-save',
    
    configureServer(server) {
      server.watcher.add(`**/*{${extensions.join(',')}}`);
      
      server.watcher.on('change', (path) => {
        if (extensions.some(ext => path.endsWith(ext))) {
          server.ws.send({
            type: 'full-reload',
            path: '*',
          });
        }
      });
    },
  };
}

实战 4:HTML 转换插件

// vite-plugin-inject-meta.ts
import { Plugin } from 'vite';
import type { IndexHtmlTransformHook } from 'vite';

export function injectMeta(tags: Record<string, string>): Plugin {
  return {
    name: 'inject-meta',
    
    transformIndexHtml: {
      enforce: 'pre',
      
      transform(html): IndexHtmlTransformHook {
        const metaTags = Object.entries(tags)
          .map(([name, content]) => 
            `<meta name="${name}" content="${content}">`
          )
          .join('\n  ');
        
        return html.replace(
          '</head>',
          `  ${metaTags}\n  </head>`,
        );
      },
    },
  };
}

使用:

// vite.config.ts
export default defineConfig({
  plugins: [
    injectMeta({
      'baidu-site-verification': 'xxx',
      'description': '我的网站',
    }),
  ],
});

实战 5:环境变量扩展

// vite-plugin-build-info.ts
import { Plugin } from 'vite';

export function buildInfo(): Plugin {
  return {
    name: 'build-info',
    
    transform(code, id) {
      if (id.includes('config.ts')) {
        return code.replace(
          /__BUILD_INFO__/g,
          JSON.stringify({
            time: new Date().toISOString(),
            version: process.env.npm_package_version,
          }),
        );
      }
    },
  };
}

调试技巧

1. 用 this.debug 输出

{
  name: 'my-plugin',
  transform(code, id) {
    if (id.includes('debug')) {
      this.debug?.('Transforming', id);
    }
  },
}

2. 用 vite-plugin-inspect

npm install -D vite-plugin-inspect
// vite.config.ts
import Inspect from 'vite-plugin-inspect';

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

然后访问 http://localhost:5173/__inspect/,可视化查看每个插件的执行顺序和耗时。

发布插件到 npm

1. package.json

{
  "name": "vite-plugin-my-thing",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "files": ["dist"],
  "keywords": ["vite-plugin", "vite"],
  "peerDependencies": {
    "vite": "^6.0.0"
  }
}

2. 构建配置

// 使用 tsup
// tsup.config.ts
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm'],
  dts: true,
  clean: true,
});

3. README 模板

# vite-plugin-my-thing

简短描述。

## 安装
\`\`\`bash
npm install -D vite-plugin-my-thing
\`\`\`

## 使用
\`\`\`typescript
import { myThing } from 'vite-plugin-my-thing';

export default defineConfig({
  plugins: [myThing()],
});
\`\`\`

## 选项
- `option1`: 描述
- `option2`: 描述

5 个常见坑

坑 1:插件顺序错误

plugins: [
  myPlugin({ enforce: 'pre' }),   // 在内置插件前
  otherPlugin(),                  // 默认顺序
  lastPlugin({ enforce: 'post' }), // 在最后
]

坑 2:忽略 sourcemap

// ❌ 没 sourcemap,调试困难
return { code: transformed };

// ✅ 加上
return {
  code: transformed,
  map: sourceMap,
};

坑 3:transform 阻塞

// ❌ 同步 transform 大文件
transform(code) {
  // 1GB 文件会卡死
}

// ✅ 用异步
async transform(code) {
  // ...
}

坑 4:忽略虚拟模块

// 处理 \0 开头的虚拟模块
if (id.startsWith('\0')) return;

坑 5:缓存问题

// 在 transform 中加 cache key
transform(code, id) {
  return {
    code: transformed,
    map,
  };
  // Vite 会基于文件路径自动缓存
  // 但如果你基于动态内容生成,需要主动失效
}

我的看法

Vite 插件开发门槛比想象低

  1. 基础插件 1 小时可上手
  2. 生产级插件 1 天可完成
  3. 发布到 npm 1 周可达

但要注意:

  • 不要重复造轮子:vite-plugin 生态已有 1000+ 插件
  • 先找现有插件:找不到再自己写
  • 保持简洁:插件只做一件事

值得自己写插件的场景

  • 团队内部统一规范(如自动注册组件)
  • 项目特有转换(如 .env 扩展)
  • 性能优化(如按需加载第三方库)

参考


本文示例基于 Vite 6.x + TypeScript 5.x,所有插件代码可直接复制使用。

📚 同主题文章

🎨 前端 / Web 分类更多