这一定是最有用的vite插件入门教程了!

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 【8月更文挑战第3天】 vite插件核心在于几个钩子函数的理解与使用,想开发vite插件,掌握这几个插件即可。本文中探讨了**config钩子**和**transformIndexHtml钩子**,相信大家看完对插件开发一定有了最基本的认识与方向!

注意看,眼前的这个人叫小帅,他来学习vite插件了!

熟悉vite的人,对vite插件的使用一定不陌生!vite插件可以帮助我们极大提升开发效率!

Vite常用插件汇总

查看社区可用插件 awesome-vite

@vitejs/plugin-vue 【官方插件】

作用: 用于编辑vue文件 注意: 该插件适用于vue3
npm
官方仓库
gitee镜像

plugin-vue-jsx 【官方插件】

作用: 添加vue3对tsx和jsx的支持
npm
官方仓库

vite-plugin-svg-icons

作用: 统一引入svg文件资源,作为图标使用
npm
官方仓库

vite-plugin-eslint

作用: 给vite加入运行时的eslint支持
npm
官方镜像

vite-plugin-mock

作用: 用于模拟后端的服务
npm
官方仓库
gitee镜像

vite-plugin-compression
笔者其他文章:vite打包优化vite-plugin-compression的使用

作用: 提供gzip压缩
npm
官方仓库

vite-plugin-html

作用: 提供index.html的压缩以及在index.html访问变量的功能
npm
官方仓库

rollup-plugin-visualizer

作用: 打包分析
npm 以及 ts支持库
官方仓库

vite-plugin-vue-setup-extend

作用: 解决vue3下 script setup语法糖 下 ,手动设置组件name不方便的问题 注意: 可能会导致vue组件debuger时,断点位置不正确问题,使用前请慎重 (直至0.4.0版本依旧有该问题)
npm
官方仓库

unplugin-vue-components

作用: 实现vue组件库的自动按需导入
npm
官方仓库

unplugin-auto-import

作用: 实现vue函数的自动导入
npm
官方仓库

cross-env

作用: 以一种通用方式,设置环境变量,解决跨平台,环境变量设置方式不同的问题
npm
官方仓库

Vite插件基础概念

vite中插件的使用非常方便,引入,使用即可。

// vite.config.js 插件使用示例
import vitePlugin from 'vite-plugin-feature'
import rollupPlugin from 'rollup-plugin-feature'

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

vite是基于rollup实现打包功能的,因此,vite插件是兼容大部分rollup插件的。

rolllup是一个打包工具,其插件是在打包时运行。

vite包括项目开发时打包时,其插件可以作用于这两种阶段。

开发Vite插件,我们需要了解Vite的独有钩子函数 (vite有与rollup相同的通用钩子)

钩子名称 释义
config 在解析 Vite 配置前调用。钩子接收原始用户配置(命令行选项指定的会与配置文件合并)和一个描述配置环境的变量,包含正在使用的 mode 和 command。
configResolved 在解析 Vite 配置后调用。使用这个钩子读取和存储最终解析的配置。
configureServer 是用于配置开发服务器的钩子。
configurePreviewServer configureServer 相同但是作为预览服务器。
transformIndexHtml 转换 index.html 的专用钩子。钩子接收当前的 HTML 字符串和转换上下文。
handleHotUpdate 执行自定义 HMR 更新处理。

这些钩子服务于特定的 Vite 目标,会被 Rollup 忽略。

现在,我们通过学习并手写几个简单插件。来更深入的学习一下vite插件开发。

vite-plugin-html的使用及实现(transformIndexHtml钩子详解)

vite-plugin-html

https://github.com/vbenjs/vite-plugin-html

插件作用

  1. HTML 压缩能力
  2. EJS模板能力
  3. 多页面应用支持
  4. 支持定制entry
  5. 支持定制index.html的模板内容

安装

yarn add vite-plugin-html -D

npm i vite-plugin-html -D

用法

添加 EJS 标签index.html,例如

<head>
  <meta charset="UTF-8" />
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title><%- title %></title>
  <%- injectScript %>
</head>

配置vite.config.ts,根据需要引入需要的功能

import {
    defineConfig, Plugin } from 'vite'
import vue from '@vitejs/plugin-vue'

import {
    createHtmlPlugin } from 'vite-plugin-html'

export default defineConfig({
   
  plugins: [
    vue(),
    createHtmlPlugin({
   
      // 是否压缩html
      minify: true,
      //在这里写完条目后,你不需要在' index.html '中添加脚本标签,原来的标签需要删除
      entry: 'src/main.ts',
      //更改index.html位置
      template: 'public/index.html',
      //需要注入index.html ejs模板的数据
      inject: {
   
        data: {
   
          title: '自定义网站标题',
          injectScript: `<script src="./inject.js"></script>`,
        },
      },
    }),
  ],
})

基础配置表说明

参数名 类型 默认值 描述
entry string src/main.ts 入口文件路径
template string index.html index.html模板路劲
inject InjectOptions - 需要注入index.html的数值
minify boolean|MinifyOptions - 是否压缩html

这个插件需要更改index.hmtl模板,因此,我们需要深入了解transformIndexHtml钩子

transformIndexHtml

类型: IndexHtmlTransformHook | { enforce?: 'pre' | 'post', transform: IndexHtmlTransformHook }

种类: 转换 index.html 的专用钩子。钩子接收当前的 HTML 字符串和转换上下文。上下文在开发期间暴露ViteDevServer实例,在构建期间暴露 Rollup 输出的包。这个钩子可以是异步的,并且可以返回以下其中之一:

  • 经过转换的 HTML 字符串
  • 注入到现有 HTML 中的标签描述符对象数组({ tag, attrs, children })。每个标签也可以指定它应该被注入到哪里(默认是在
之前)
  • 一个包含 { html, tags } 的对象
  • 基础示例:

    const htmlPlugin = () => {
       
      return {
       
        name: 'html-transform',
        transformIndexHtml(html) {
       
          return html.replace(
            /<title>(.*?)</title>/,
            `<title>Title replaced!</title>`
          )
        }
      }
    }
    

    完整钩子签名:

    type IndexHtmlTransformHook = (
      html: string,
      ctx: {
       
        path: string
        filename: string
        server?: ViteDevServer
        bundle?: import('rollup').OutputBundle
        chunk?: import('rollup').OutputChunk
      }
    ) =>
      | IndexHtmlTransformResult
      | void
      | Promise<IndexHtmlTransformResult | void>
    
    type IndexHtmlTransformResult =
      | string
      | HtmlTagDescriptor[]
      | {
       
          html: string
          tags: HtmlTagDescriptor[]
        }
    
    interface HtmlTagDescriptor {
       
      tag: string
      attrs?: Record<string, string>
      children?: string | HtmlTagDescriptor[]
      /**
       * 默认: 'head-prepend'
       */
      injectTo?: 'head' | 'body' | 'head-prepend' | 'body-prepend'
    }
    

    我相信第一次看完上完的解释,你一定会一头雾水,没关系,我们手写个简单版实现!

    简易版实现

    我们在项目 代码中创建plugin文件夹,并创建CreateHtmlPlugin js文件

    module.exports = (options) => {
       
      return {
       
        // 转换html的
        transformIndexHtml: {
       
          // 将我们插件的一个执行时机提前
          enforce: "pre",
          transform: (html, ctx) => {
       
            // 聪明的你一定看懂其实是通过正则匹配特定字符来替换内容了
            return html.replace(/<%= title %>/g, options.inject.data.title);
          }
        }
      };
    };
    

    这里的html就是模板内的html内容字符串,此时,项目内即可使用:

    index.html模板内

    <title><%= title %></title>
    

    现在,你应该能看懂上面 transformIndexHtml钩子的介绍了!**

    最后,我们在vite.config.js中引入我们写的插件

    // vite.config.js
    import CreateHtmlPlugin from "./plugin/CreateHtmlPlugin";
    import {
        defineConfig } from "vite";
    export default defineConfig({
       
      plugins: [
        CreateHtmlPlugin({
       
          inject: {
       
            data: {
       
              title: "好好学习,天天向上"
            }
          }
        })
      ]
    });
    

    大功告成!

    vite-aliases的使用与原理(config钩子)详解

    vite-aliases的主要作用

    vite-aliases可以帮助我们自动生成别名:检测你当前目录下包括src在内的所有文件夹, 并帮助我们去生成别名

    笔者关于本插件的文章:# 什么年代了,你还在手动配置vite路径别名?

    原理

    Vite-aliases其实是抢在vite执行配置文件之前去改写配置文件

    通过vite.config.js 返回出去的配置对象以及我们在插件的config生命周期中返回的对象都不是最终的一个配置对象,vite会把这几个配置对象进行一个merge合并 {...defaultConfig, . ..specifyConfig}

    Vite-aliases实际就是帮我导出了一个默认的resolve.alias配置,最终和vite.config.js内的配置进行合并。

    手写Vite-aliases

    项目搭建

    创建项目,安装vite依赖

    vite-study
    │  ├─ src
    │  │  ├─ assets
    │  │  │  └─ bac.jpg
    │  │  └─ main.js
    │  ├─ index.html            
    │  ├─ package.json
    │  └─ vite.config.js
    

    index.html内引入main.js

    <script src="./src/main.js" type="module"></script>
    

    main.js中,我们使用路径别名引入一张图片

    import bac from "@assets/bac.jpg";
    console.log("bac: ", bac);
    
    const img = document.createElement("img");
    img.src = bac;
    document.body.appendChild(img);
    

    现在,我们创建vite-aliases插件,使main.js内@路径别名生效。

    Vite-aliases通过覆盖resolve.alias配置生效,因此需要借助使用config钩子。

    插件基本结构与config钩子

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

    种类: 在解析 Vite 配置前调用。

    钩子接收原始用户配置config和一个描述配置环境的变量env,env包含正在使用的 modecommand两个对象。

    它可以返回一个将被深度合并到现有配置中的部分配置对象UserConfig,或者直接改变配置。

    我们在项目创建plugin/vite-aliases.js文件,然后再vite.config.js引入插件

    vite-study
    ├─ plugin
    │  │  └─ vite-aliases.js
    │  ├─ src
    │  │  ├─ assets
    │  │  │  └─ bac.jpg
    │  │  └─ main.js
    │  ├─ index.html            
    │  ├─ package.json
    │  └─ vite.config.js
    
    // vite.config.js
    import viteAliases from "./plugin/vite-aliases";
    import {
        defineConfig } from "vite";
    export default defineConfig({
       
      plugins: [viteAliases()]
    });
    

    根据config钩子的定义,写出插件的基本结构

    // vite-aliases
    module.exports = () => {
       
        return {
       
            config(config, env) {
       
                console.log("config", config);
                console.log("env", env);
                return {
       
                    resolve: {
       
                        alias: "XXXXXX"
                    }
                };
            }
        }
    }
    

    config钩子的参数

    我们分别执行 npm run dev (vite) 和npm run build (vite build)两个命令,在命令终端看看config和env的输出结果

    • npm run dev输出结果
    config {
       
      plugins: [ {
        config: [Function: config] } ],
      optimizeDeps: {
        force: undefined },
      server: {
       }
    }
    env {
        mode: 'development', command: 'serve', ssrBuild: false }
    
    • npm run build输出结果
    config {
       
      plugins: [ {
        config: [Function: config] } ],
      optimizeDeps: {
        force: undefined },
      build: {
       }
    } 
    env {
        mode: 'production', command: 'build', ssrBuild: false }
    

    可见,不同环境下,modecommand值是不同的。

    完善内容

    篇幅问题,只贴最终代码,大家自己手写试试!

    // vite的插件必须返回给vite一个配置对象
    const fs = require("fs");
    const path = require("path");
    // 2.1 根据 fs.readdirSync 获取的文件数组信息,将文件夹名及文件名进行拆分,返回一个信息数组
    function diffDirAndFile(dirFilesArr = [], basePath = "") {
       
        const result = {
        dirs: [], files: []}
        dirFilesArr.forEach(name => {
       
            // 我直接用异步的方式去写的
            const currentFileStat = fs.statSync(path.resolve(__dirname, basePath + "/" + name));
            // currentFileStat.isDirectory() 判断是否文件夹
            // 根据是文件夹还是文件进行 ,向result添加信息
            if (currentFileStat.isDirectory()) {
       
                result.dirs.push(name);
            } else {
       
                result.files.push(name);
            }
        })
        return result;
    }
    // 2.获取src下的所有文件目录并生成@路径别名配置
    function getTotalSrcDir(keyName) {
       
        //获取src目录下的所有目录及文件
        const result = fs.readdirSync(path.resolve(__dirname, "../src"));
        // 2.1获取我们处理好的文件对象 {}.dirs存放文件夹名数组 {}.files存放文件名数组
        const diffResult = diffDirAndFile(result, "../src");
        const resolveAliasesObj = {
       }; // 用来存放一个一个的别名配置 @assets: xxx
        // diffResult.dirs src下的文件夹名 遍历文件名,生成配置
        diffResult.dirs.forEach(dirName => {
       
            // 别名路径 如:@assets
            const key = `${
         keyName}${
         dirName}`;
            // 别名路径对应的 真是路径
            const absPath = path.resolve(__dirname, "../src" + "/" + dirName);
            resolveAliasesObj[key] = absPath;
        })
        return resolveAliasesObj;
    }
    
    module.exports = ({
        keyName = "@" } = {
       }) => {
       
      return {
       
        config(config, env) {
       
          // config函数可以返回一个对象, 这个对象是部分的vite.config.js的配置【我们需要更改的部分】
          // 1.通过自定义函数获取 alias 完整配置。keyName作为可以修改的@控制符
          const resolveAliasesObj = getTotalSrcDir(keyName);
          return {
       
            // 在这我们要返回一个resolve出去, 将src目录下的所有文件夹进行别名控制
            resolve: {
       
              alias: resolveAliasesObj
            }
          };
        }
      };
    };
    

    此时,执行npm run dev,就会发现页面已经正确将图片展示。路径别名生效了。

    总结

    vite插件核心在于几个钩子函数的理解与使用,想开发vite插件,掌握这几个插件即可。本文中探讨了config钩子transformIndexHtml钩子,相信大家看完对插件开发一定有了最基本的认识与方向!

    大家看完有收获,一定要一键三连啊!不要下次一定!

    相关文章
    |
    JavaScript
    搭建Vue3组件库:第四章 使用Vitepress搭建文档网站
    文档建设一般会是一个静态网站的形式 ,这次采用 Vitepress 完成文档建设工作。 Vitepress 是一款基于Vite 的静态站点生成工具。开发的初衷就是为了建设 Vue 的文档。Vitepress 的方便之处在于,可以使用流行的 Markdown 语法进行编写,也可以直接运行 Vue 的代码。也就是说,它能很方便地完成展示组件 Demo 的任务。
    1658 0
    搭建Vue3组件库:第四章 使用Vitepress搭建文档网站
    |
    3月前
    |
    JavaScript Java 测试技术
    基于springboot+vue.js+uniapp的小说阅读平台附带文章源码部署视频讲解等
    基于springboot+vue.js+uniapp的小说阅读平台附带文章源码部署视频讲解等
    51 0
    |
    4月前
    |
    JavaScript Java 测试技术
    基于springboot+vue.js+uniapp小程序的在线小说阅读平台附带文章源码部署视频讲解等
    基于springboot+vue.js+uniapp小程序的在线小说阅读平台附带文章源码部署视频讲解等
    30 1
    |
    4月前
    |
    JavaScript Java 测试技术
    基于ssm+vue.js+uniapp小程序的小说阅读平台附带文章和源代码部署视频讲解等
    基于ssm+vue.js+uniapp小程序的小说阅读平台附带文章和源代码部署视频讲解等
    28 5
    |
    缓存 前端开发 JavaScript
    浅浅阅读umi中InitialState插件源码 - 杨磊
    InitialState插件源码的简要介绍
    994 0
    浅浅阅读umi中InitialState插件源码 - 杨磊
    |
    5月前
    |
    资源调度 JavaScript 搜索推荐
    《VitePress 简易速速上手小册》第9章 VitePress 的扩展与插件(2024 最新版)
    《VitePress 简易速速上手小册》第9章 VitePress 的扩展与插件(2024 最新版)
    365 0
    |
    5月前
    |
    JavaScript 前端开发 开发工具
    Nodejs 第二十二章(脚手架)
    Nodejs 第二十二章(脚手架)
    81 0
    |
    12月前
    |
    API 开发者
    🚀两个简单的自定义插件,探究Vite的插件机制
    🚀两个简单的自定义插件,探究Vite的插件机制
    |
    缓存 前端开发 JavaScript
    浅浅阅读umi中InitialState插件源码
    InitialState插件源码的简要介绍
    640 1
    浅浅阅读umi中InitialState插件源码
    |
    JavaScript
    vue项目中如何使用markdown编辑器插件
    vue项目中如何使用markdown编辑器插件
    438 0