Vite 从入门到精通 | 插件系统

1,793 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

Vite 从入门到精通 | 插件系统

介绍

命名规范

  • rollup-plugin-xxx
  • vite-plugin-xxx

Vite 兼容 Rollup 钩子

  • 服务启动时 (启动 server 时执行一次, 与文件更新无关)
    • options
    • buildStart
  • 模块 (找到对应的文件,->加载->转变目标代码)
    • resolveId
    • load
    • transform
  • 服务关闭时
    • buildEnd
    • closeBundle

modulePased 是不会被调用的, 防止 Vite 整体对代码执行 AST 解析

rollup 插件可以在 build中配置

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 引入 jsx 依赖
import vueJsx from '@vitejs/plugin-vue-jsx'

// https://vitejs.dev/config/
export default defineConfig({
 
  plugins: [ // 使用插件
    vue(),
    vueJsx(),// 使用 vue-jsx
  ],
  build: {
    rollupOptions:{
      plugins
    }
  }
})

条件: rollup插件符合 vite 使用条件

  • 没有使用 moduleParsed 钩子
  • 在打包钩子和输出钩子之间没有很强的耦合

Vite 钩子

  • config
  • configResolved
  • configureServer
  • transformIndexHtml
  • handleHotUpdate

Vite 插件执行时机

  • Pre: 最先被执行的插件 , vite 使用 rolluo alias 插件之后就会按照plugin数组中定义的顺序的去执行
  • Normal: vite 核心插件执行之后, vite build 插件执行之前执行
  • post: vite build 插件 之后执行

实操

  • vite 项目创建 plugins/test-plugin.ts

    // test-plugin.ts
    export default (enforce?: 'pre' | 'post') => {
    
        return {
            name: 'test',
            enforce,
    
            buildStart() {
                console.log("buildStart", enforce)
            },
            resolveId(){
                console.log('resolveId',enforce)
            }
        }
    }
    
  • vite.config.js 配置

    // vim vite.config.js
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    // 引入 jsx 依赖
    import vueJsx from '@vitejs/plugin-vue-jsx'
    import testPlugin from './plugins/test-plugin'
    // https://vitejs.dev/config/
    export default defineConfig({
     plugins: [
       vue(),
       vueJsx(),
       testPlugin('post'),
       testPlugin(),
       testPlugin('pre')
      ]
    })
    
  • 输出结果

    $ yarn dev
    yarn run v1.22.4
    warning package.json: No license field
    $ vite
    buildStart pre
    buildStart undefined
    buildStart post
    
      vite v2.6.2 dev server running at:
    
      > Local: http://localhost:3000/
      > Network: use `--host` to expose
    
      ready in 415ms.
    
    > Pre > normal > post
    

Vite 插件 API (钩子)

  • config: return 一个新的 config 与 vite.config.js deepMerge

    import { Plugin } from "vite";
    export default (enforce?: "pre" | "post"): Plugin => {
      return {
        name: "test",
        // userConfig vite.config.js 中的配置项
        config(userConfig) {
            // return 一个新的 config 与 vite.config.js deepMerge
            return {
                resolve: {
                    alias: {
                        "@img": '/src/img'
                    }
                }
            }
        },
      };
    };
    
    
  • configResolved

    import { Plugin } from "vite";
    export default (enforce?: "pre" | "post"): Plugin => {
      return {
        name: "test",
        // config: 最终的 config
        configResolved(config){
      			// 打印 最终 config 中的 plugins 
            console.log(config.plugins)
        }
      };
    };
    
    

    得到结果

    [
      {
        name: 'vite:pre-alias',
      },
      {
        name: 'alias',
      },
      {
        name: 'vite:modulepreload-polyfill',
      },
      {
        name: 'vite:resolve',
      },
      {
        name: 'vite:html',
      },
      {
        name: 'vite:css',
      },
      {
        name: 'vite:esbuild',
      },
      { name: 'vite:json'},
      {
        name: 'vite:wasm',
      },
      {
        name: 'vite:worker',
      },
      {
        name: 'vite:asset',
      },
      {
        name: 'vite:vue',
        handleHotUpdate: [Function: handleHotUpdate],
        config: [Function: config],
        configResolved: [Function: configResolved],
        configureServer: [Function: configureServer],
        resolveId: [AsyncFunction: resolveId],
        load: [Function: load],
        transform: [Function: transform]
      },
      {
        name: 'vite:vue-jsx',
      },
      { name: 'test', },
      { name: 'vite:define',},
      {
        name: 'vite:css-post',
      },
      { name: 'vite:client-inject',},
      {
        name: 'vite:import-analysis',
      }
    ]
    
  • configureServer

      configureServer(server){
            // 增加中间件
            // 会在 Vite 中间件之前执行
            server.middlewares.use((req,res,next)=> {
                if (req.url === "/test") {
                    res.end("Hello Vite Plugin")
                } else {
                    next()
                }
            })
        }
    

    image-20211003221037986.png

  • transformIndexHtml

    transformIndexHtml(html) {
      // 在这里可以处理 index.html
      console.log(html)
    }
    
    
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <link rel="icon" href="/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Vite App</title>
      </head>
      <body>
        <div id="app"><!--APP_HTML--></div>
        <script type="module" src="/src/main.js"></script>
      </body>
    </html>
    
    // 处理 html
    transformIndexHtml(html) {
            return html.replace('app','root')
    }
    

    image-20211003221445951.png

  • handleHotUpdate

    handleHotUpdate (ctx) {
      // 文件更新的所有信息
      console.log(ctx)
    }