前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vite插件开发指南:轻松驾驭前端开发利器

Vite插件开发指南:轻松驾驭前端开发利器

作者头像
微芒不朽
发布2024-07-20 12:25:54
1190
发布2024-07-20 12:25:54
举报
文章被收录于专栏:前端进阶-詹躲躲

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

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

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

插件名称规则

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

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

vite-plugin-*

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

插件配置

代码语言:javascript
复制
// vite.config.js
import vitePlugin from 'vite-plugin-feature'
import rollupPlugin from 'rollup-plugin-feature'

export default defineConfig({
  plugins: [vitePlugin(), rollupPlugin()],
})

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

插件扁平化

代码语言:javascript
复制
// 框架插件
import frameworkRefresh from 'vite-plugin-framework-refresh'
import frameworkDevtools from 'vite-plugin-framework-devtools'

export default function framework(config) {
  return [frameworkRefresh(config), frameworkDevTools(config)]
}

使用插件

代码语言:javascript
复制
// 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 配置前调用。钩子接收原始用户配置(命令行选项指定的会与配置文件合并)和一个描述配置环境的变量,包含正在使用的 modecommand。它可以返回一个将被深度合并到现有配置中的部分配置对象,或者直接改变配置(如果默认的合并不能达到预期的结果)。示例:js

代码语言:javascript
复制
// 返回部分配置(推荐)
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 配置后调用。使用这个钩子读取和存储最终解析的配置。当插件需要根据运行的命令做一些不同的事情时,它也很有用。

代码语言:javascript
复制
const examplePlugin = () => {
  let config

  return {
    name: 'read-config',

    configResolved(resolvedConfig) {
      // 存储最终解析的配置
      config = resolvedConfig
    },

    // 在其他钩子中使用存储的配置
    transform(code, id) {
      if (config.command === 'serve') {
        // dev: 由开发服务器调用的插件
      } else {
        // build: 由 Rollup 调用的插件
      }
    },
  }
}
一个简单的插件示例
代码语言:javascript
复制
{
  // 插件名称
  name: 'vite-plugin-xxx',
  load(code) {
   // 钩子逻辑
  },
}

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

代码语言:javascript
复制
// 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 兼容的钩子,这个钩子主要分为三个阶段:

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

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

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

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

代码语言:javascript
复制
config\plugins\unplugin-auto-import.js
代码语言:javascript
复制
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

代码语言:javascript
复制
config\plugins\unplugin-vue-define-options.js
代码语言:javascript
复制
import DefineOptions from 'unplugin-vue-define-options/vite' // 使用defineOptions
const options = (viteEnv = {}) => {
    return DefineOptions()
}
export { options }

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

代码语言:javascript
复制
config\plugins\vite-plugin-ployfill.js
代码语言:javascript
复制
export default function vitePluginPloyfill() {
    return {
        name: 'vite-plugin-ployfill', // 插件名称
        apply: 'build', // 指定插件的应用阶段
        transformIndexHtml(html) {
            const script = `
          <script>
            (function() {
              if (typeof EventTarget !== 'undefined') {
                const func = EventTarget.prototype.addEventListener;
                EventTarget.prototype.addEventListener = function(type, fn, capture) {
                  this.func = func;
                  if (typeof capture !== 'boolean') {
                    capture = capture || {};
                    capture.passive = false;
                  }
                  this.func(type, fn, capture);
                };
              }
            }());
          </script>
        `
            return html.replace('</body>', `${script}</body>`)
        }
    }
}

export { vitePluginPloyfill }

封装成插件的所有插件

注册插件

代码语言:javascript
复制
config\index.js

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

代码语言:javascript
复制
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)
    })
}

插件配置

代码语言:javascript
复制
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

代码语言:javascript
复制
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中。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 叫我詹躲躲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 插件名称规则
  • vite-plugin-*
  • 插件配置
  • 插件扁平化
  • 使用插件
  • 通用钩子
  • Vite 独有钩子
    • config
      • configResolved
        • 一个简单的插件示例
    • 全局自动导入插件 unplugin-auto-import/vite
    • 第二个插件 unplugin-vue-define-options/vite
    • 插入组件到全局 vite-plugin-ployfill
    • 封装成插件的所有插件
    • 注册插件
    • 加载plugins文件夹下面的所有的插件
    • 插件配置
    • 使用插件
    • vite.config.js
    相关产品与服务
    消息队列 TDMQ
    消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档