likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 37

注意看,眼前的这个人叫小帅,他来学习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镜像

作用: 提供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是用于配置开发服务器的钩子。
configurePreviewServerconfigureServer 相同但是作为预览服务器。
transformIndexHtml转换 index.html 的专用钩子。钩子接收当前的 HTML 字符串和转换上下文。
handleHotUpdate执行自定义 HMR 更新处理。

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

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

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

vite-plugin-html

github.com/vbenjs/vite…

插件作用

  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>`,
        },
      },
    }),
  ],
})

基础配置表说明

参数名类型默认值描述
entrystringsrc/main.ts入口文件路径
templatestringindex.htmlindex.html模板路劲
injectInjectOptions-需要注入index.html的数值
minifyboolean|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-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钩子,相信大家看完对插件开发一定有了最基本的认识与方向!

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

转载自:https://juejin.cn/post/7270528132167417915
评论
请登录