Vite插件开发秘籍:解锁前端开发新姿势,轻松应对挑战!
创始人
2025-01-08 19:05:32
0次

Vite 插件扩展了设计出色的 Rollup 接口,带有一些 Vite 独有的配置项。因此,你只需要编写一个 Vite 插件,就可以同时为开发环境和生产环境工作。

当创作插件时,你可以在 vite.config.js 中直接使用它。

如果插件不使用 Vite 特有的钩子,可以作为 兼容 Rollup 的插件 来实现,推荐使用 Rollup 插件名称约定。

插件名称规则

  • Rollup 插件应该有一个带 rollup-plugin- 前缀、语义清晰的名称。
  • 在 package.json 中包含 rollup-plugin 和 vite-plugin 关键字。

这样,插件也可以用于纯 Rollup 或基于 WMR 的项目。
对于 Vite 专属的插件:

vite-plugin-*

  • Vite 插件应该有一个带 vite-plugin- 前缀、语义清晰的名称。
  • 在 package.json 中包含 vite-plugin 关键字。
  • 在插件文档增加一部分关于为什么本插件是一个 Vite 专属插件的详细说明(如,本插件使用了 Vite 特有的插件钩子)。

插件配置

// vite.config.js import vitePlugin from 'vite-plugin-feature' import rollupPlugin from 'rollup-plugin-feature'  export default defineConfig({   plugins: [vitePlugin(), rollupPlugin()], }) 

假值的插件将被忽略,可以用来轻松地启用或停用插件。
plugins 也可以接受将多个插件作为单个元素的预设。这对于使用多个插件实现的复杂特性(如框架集成)很有用。该数组将在内部被扁平化(flatten)。

插件扁平化

// 框架插件 import frameworkRefresh from 'vite-plugin-framework-refresh' import frameworkDevtools from 'vite-plugin-framework-devtools'  export default function framework(config) {   return [frameworkRefresh(config), frameworkDevTools(config)] } 

使用插件

// vite.config.js import { defineConfig } from 'vite' import framework from 'vite-plugin-framework'  export default defineConfig({   plugins: [framework()], }) 

通用钩子

在开发中,Vite 开发服务器会创建一个插件容器来调用 Rollup 构建钩子,与 Rollup 如出一辙。
以下钩子在服务器启动时被调用:

  • options
  • buildStart

以下钩子会在每个传入模块请求时被调用:

  • resolveId
  • load
  • transform

它们还有一个扩展的 options 参数,包含其他特定于 Vite 的属性。你可以在 SSR 文档 中查阅更多内容。
一些 resolveId 调用的 importer 值可能是根目录下的通用 index.html 的绝对路径,这是由于 Vite 非打包的开发服务器模式无法始终推断出实际的导入者。对于在 Vite 的解析管道中处理的导入,可以在导入分析阶段跟踪导入者,提供正确的 importer 值。
以下钩子在服务器关闭时被调用:

  • buildEnd
  • closeBundle

请注意 moduleParsed 钩子在开发中是 不会 被调用的,因为 Vite 为了性能会避免完整的 AST 解析。

Vite 独有钩子

Vite 插件也可以提供钩子来服务于特定的 Vite 目标。这些钩子会被 Rollup 忽略。

config

  • 类型:(config: UserConfig, env: { mode: string, command: string }) => UserConfig | null | void
  • 种类:async, sequential

在解析 Vite 配置前调用。钩子接收原始用户配置(命令行选项指定的会与配置文件合并)和一个描述配置环境的变量,包含正在使用的 mode 和 command。它可以返回一个将被深度合并到现有配置中的部分配置对象,或者直接改变配置(如果默认的合并不能达到预期的结果)。
示例:
js

// 返回部分配置(推荐) const partialConfigPlugin = () => ({   name: 'return-partial',   config: () => ({     resolve: {       alias: {         foo: 'bar',       },     },   }), })  // 直接改变配置(应仅在合并不起作用时使用) const mutateConfigPlugin = () => ({   name: 'mutate-config',   config(config, { command }) {     if (command === 'build') {       config.root = 'foo'     }   }, }) 

注意
用户插件在运行这个钩子之前会被解析,因此在 config 钩子中注入其他插件不会有任何效果。

configResolved

在解析 Vite 配置后调用。使用这个钩子读取和存储最终解析的配置。当插件需要根据运行的命令做一些不同的事情时,它也很有用。

const examplePlugin = () => {   let config    return {     name: 'read-config',      configResolved(resolvedConfig) {       // 存储最终解析的配置       config = resolvedConfig     },      // 在其他钩子中使用存储的配置     transform(code, id) {       if (config.command === 'serve') {         // dev: 由开发服务器调用的插件       } else {         // build: 由 Rollup 调用的插件       }     },   } } 
一个简单的插件示例
{ 	 // 插件名称 	 name: 'vite-plugin-xxx', 	 load(code) { 	 	// 钩子逻辑 	 }, } 

如果插件是一个 npm 包,在 package.json 中的包命名也推荐以 vite-plugin 开头
一般情况下因为要考虑到外部传参,我们不会直接写一个对象,而是实现一个返回插件对象的 工厂函数,如下代码所示

// myPlugin.js export function myVitePlugin(options) { 	 console.log(options) 	 return { 		 name: 'vite-plugin-xxx', 		 load(id) { 		 	// 在钩子逻辑中可以通过闭包访问外部的 options 传参 		 } 	 } }  // 使用方式 // vite.config.ts import { myVitePlugin } from './myVitePlugin'; export default {  plugins: [myVitePlugin({ /* 给插件传参 */ })] }  

其中 Vite 会调用一系列与 Rollup 兼容的钩子,这个钩子主要分为三个阶段:

服务器启动阶段: options 和 buildStart 钩子会在服务启动时被调用 请求响应阶段: 当浏览器发起请求时,Vite 内部依次调用 resolveId 、 load 和 transform 钩子 服务器关闭阶段: Vite 会依次执行 buildEnd 和 closeBundle 钩子 

config : 用来进一步修改配置
configResolved : 用来记录最终的配置信息
configureServer : 用来获取 Vite Dev Server 实例,添加中间件
transformIndexHtml : 用来转换 HTML 的内容
handleHotUpdate : 用来进行热更新模块的过滤,或者进行自定义的热更新处理

全局自动导入插件 unplugin-auto-import/vite

本身引入的已经是一个插件,由此这里直接导出这个插件

config\plugins\unplugin-auto-import.js 
import autoImport from 'unplugin-auto-import/vite' // 全局自动导入插件 const AutoImport = (viteEnv = {}) => {     return autoImport({         imports: [             'vue', // 全局自动导入vue             'vue-router', // 全局自动导入路由插             'pinia',             '@vueuse/core'         ],         dts: 'config/auto-imports.d.ts'     }) }  export { AutoImport } 

第二个插件 unplugin-vue-define-options/vite

config\plugins\unplugin-vue-define-options.js 
import DefineOptions from 'unplugin-vue-define-options/vite' // 使用defineOptions const options = (viteEnv = {}) => {     return DefineOptions() } export { options } 

插入组件到全局 vite-plugin-ployfill

config\plugins\vite-plugin-ployfill.js 
export default function vitePluginPloyfill() {     return {         name: 'vite-plugin-ployfill', // 插件名称         apply: 'build', // 指定插件的应用阶段         transformIndexHtml(html) {             const script = `                    `             return html.replace('', `${script}`)         }     } }  export { vitePluginPloyfill } 

封装成插件的所有插件

注册插件

config\index.js 

加载plugins文件夹下面的所有的插件

const loadPluginModules = async() => {     return new Promise(async(success) => {         const modulesPath = resolve(__dirname, 'plugins')         const fileNames = fs.readdirSync(modulesPath)         const modulesImprot = []         fileNames.forEach(async(item) => {             const filePath = join(modulesPath, item)             if (                 fs.statSync(filePath).isFile() &&         ['.js'].includes(extname(filePath))             ) {                 modulesImprot.push(import(`./config/plugins/${item}`))             }         })          const modules = await Promise.all(modulesImprot)         success(modules)     }) } 

插件配置

const pluginCfg = async(viteEnv = {}) => {     const modulesArr = [vue(), vueJsx()]     const modules = await loadPluginModules()     modules.forEach((item) => {         const funcs = Object.values(item)         funcs.forEach((it) => {             modulesArr.push(it(viteEnv))         })     })     return modulesArr } 

这里的插件函数,在vite.config.js中异步调用

使用插件

vite.config.js

import { defineConfig, loadEnv } from 'vite' import { baseCfg, pluginCfg, svgBuilder } from './config' export default async({ mode }) => {     return defineConfig({         ...baseCfg({ mode, loadEnv }),         plugins: [             svgBuilder('./src/icons/svg/'),             ...(await pluginCfg())         ]     }) } 

备注:基本配置在baseCfg中,所有封装的插件在pluginCfg函数中返回到plugins中。

相关内容

热门资讯

实测交流透视挂!宝玛大厅ai智... 实测交流透视挂!宝玛大厅ai智能透视挂教程,宝玛大厅破解软件经验(本来有挂辅助)1、每一步都需要思考...
总算了解辅助器!朱雀吴江三缺一... 总算了解辅助器!朱雀吴江三缺一ai智能透视辅助挂技巧,朱雀吴江三缺一好像是有挂(有辅助挂细节)朱雀吴...
今日公布透视!九五至尊大厅ai... 今日公布透视!九五至尊大厅ai智能透视插件,九五至尊大厅有没有透视挂窍门(切实有挂技术)1、在九五至...
玩家必看科普开挂!聚闲麻将ai... 玩家必看科普开挂!聚闲麻将ai智能透视辅助挂软件,聚闲麻将总是真的有挂(有辅助挂方式)聚闲麻将能透视...
据报道开挂!旅游娱乐ai智能开... 据报道开挂!旅游娱乐ai智能开挂工具,旅游娱乐辅助工具演示(一直有挂教学)1、全新机制【旅游娱乐ai...
一分钟揭秘开挂!决胜朋来麻将a... 一分钟揭秘开挂!决胜朋来麻将ai智能透视辅助挂教程,决胜朋来麻将真是真的是有挂(有辅助挂总结)1、首...
如何分辨真伪透视!红牛ai智能... 如何分辨真伪透视!红牛ai智能透视攻略,红牛是不是有挂经验(真是有挂技术)1、操作简单,无需红牛手机...
2分钟细说辅助器!江湖大庆麻将... 2分钟细说辅助器!江湖大庆麻将ai智能透视辅助挂攻略,江湖大庆麻将竟然是真的有挂(有辅助挂头条)1、...
总算清楚透视挂!老夫子厅ai智... 总算清楚透视挂!老夫子厅ai智能透视挂技巧,老夫子厅怎么开挂课程(好像发现有挂)老夫子厅破解侠是真的...
解密关于辅助!菜鸟麻将ai智能... 解密关于辅助!菜鸟麻将ai智能透视辅助挂脚本,菜鸟麻将总是存在有挂(有辅助挂存在)1、用户打开应用后...