注意看,眼前的这个人叫小帅,他来学习vite插件了!
熟悉vite的人,对vite插件的使用一定不陌生!vite插件可以帮助我们极大提升开发效率!
Vite常用插件汇总
查看社区可用插件 awesome-vite。
@vitejs/plugin-vue 【官方插件】
plugin-vue-jsx 【官方插件】
vite-plugin-svg-icons
vite-plugin-eslint
vite-plugin-mock
vite-plugin-compression
笔者其他文章:vite打包优化vite-plugin-compression的使用
vite-plugin-html
rollup-plugin-visualizer
vite-plugin-vue-setup-extend
作用: 解决vue3下 script setup语法糖 下 ,手动设置组件name不方便的问题 注意: 可能会导致vue组件debuger时,断点位置不正确问题,使用前请慎重 (直至0.4.0版本依旧有该问题)
npm
官方仓库
unplugin-vue-components
unplugin-auto-import
cross-env
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
插件作用
- HTML 压缩能力
- EJS模板能力
- 多页面应用支持
- 支持定制entry
- 支持定制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 })。每个标签也可以指定它应该被注入到哪里(默认是在
基础示例:
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包含正在使用的 mode 和 command两个对象。
它可以返回一个将被深度合并到现有配置中的部分配置对象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 }
可见,不同环境下,mode和 command值是不同的。
完善内容
篇幅问题,只贴最终代码,大家自己手写试试!
// 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钩子,相信大家看完对插件开发一定有了最基本的认识与方向!
大家看完有收获,一定要一键三连啊!不要下次一定!