前言
上一章前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理讲解了vite
对静态资源的开箱即用与css
模块化配置,比起webpack
那么多约束,vite
可谓是抛弃了那么沉重的包袱,这一章我们再来一起探索以下vite
的插件系统,这将会使其又上升一个台阶。
插件是什么
就webpack
来说,plugins
的作用在于强化其构建过程中,所遇到的一些工程化的问题,比如代码压缩
,资源压缩
等,所以vite
作为新时代的构建工具,理应当也具有插件系统来解决构建项目的整个生命周期
中所遇到的工程化的问题,说白了,插件就是为了解决某一类型
的问题而出现的一个
或一种
工具函数。比如lodash
,他被称之为一个库,也可以认作是一个插件。所以vite
会在不同的生命周期
中调用不同的插件
去达成不同的目的。
插件系统初体验
那么我们了解了插件是什么之后,我们来认识第一个插件:vite-aliases
-
作用:根据项目路径配置,自动生成路径别名。
-
好处:不用再自己配置
resolve
了。 -
安装
npm i vite-aliases -D
- 在
vite.config.js
中添加
// vite.config.js
import { ViteAliases } from 'vite-aliases'
export default defineConfig({
...
plugins: [
ViteAliases()
]
})
- 插件只支持
ESM
,所以需要在package.json
里面添加
{
"type": "module"
}
- 结果:
- 项目中的目录结构
- src
- - assets
- - components
- - pages
- - store
- - utils
根据路径生成如下配置,解释:如果在项目中引入资源import xx from '@/',那么他会直接去src目录找。
[
{
find: '@',
replacement: '${your_project_path}/src'
},
{
find: '@assets',
replacement: '${your_project_path}/src/assets'
},
{
find: '@components',
replacement: '${your_project_path}/src/components'
},
{
find: '@pages',
replacement: '${your_project_path}/src/pages'
},
{
find: '@store',
replacement: '${your_project_path}/src/store'
},
{
find: '@utils',
replacement: '${your_project_path}/src/utils'
}
]
更多配置请查看https://github.com/subwaytime/vite-aliases#configuration
vite-aliases源码解读
首先讲一下为什么要去读这个插件源码,原因有两点:
- 其一,学习源码有助于我们提高自身编码水平,吸收别人的编程思维。
- 其二,学东西一定要知其然而知其所以然,稍微了解皮毛是肯定不行的。当然了,阅读这个插件源码,肯定是需要去了解他背后的一个实现逻辑,有助于我们自己开发一个插件。 目录:
在package.json
里面运行npm run build
,就会在根目录生成dist
文件夹,也就是github
上的包。但是它使用tsup
来进行打包的,不懂得同学可以戳tsup文档,我们打开vite-aliases
的源码,目录:./vite-aliases/src/index.ts
:
export function ViteAliases(options: Partial<Options> = {}): PluginOption {
let gen: Generator; // Generator类
return {
name: 'vite-aliases', // 插件名字
enforce: 'pre', // 插件执行顺序
config(config, { command }) { // vite特有的config钩子,config为默认配置,command为命令
gen = new Generator(command, options);
gen.init(); // 调用init
config.resolve = {
alias: config.resolve?.alias
? [
...toArray(config.resolve.alias as any),
...gen.aliases
]
: gen.aliases,
};
},
};
};
这里很明显,他跟webpack
一样, 请戳 >>> 重学webpack系列(四) -- webpack的plugins机制的解读,也是注入在vite
执行过程中所暴露的生命周期钩子里面执行的,但是与webpack
的区别就是,vite
的插件必须返回一个对象
,vite
会在解析vite.config.js
的时候(config
钩子就是在解析vite.config.js
之前执行),遍历plugins
数组,找到可以执行的东西。既然了解了vite
插件怎么去注入到vite
构建流程中,那么我们现在来看看插件的具体实现。
ViteAliases
import type { PluginOption } from 'vite';
import { Generator } from './generator';
import type { Options } from './types';
import { toArray } from './utils';
export function ViteAliases(options: Partial<Options> = {}): PluginOption {
let gen: Generator; // Generator类
return {
name: 'vite-aliases', // 插件名字
enforce: 'pre', // 插件执行顺序
config(config, { command }) { // vite特有的config钩子,config为默认配置,command为命令
gen = new Generator(command, options);
gen.init(); // 调用init,读取root目录,生成数组对象
config.resolve = {
alias: config.resolve?.alias // 有alias就合并
? [
...toArray(config.resolve.alias as any),
...gen.aliases
]
: gen.aliases, // 没有就直接用生成的alias
};
},
};
};
ViteAliases
函数被默认导出,config
参数为整个vite.config.js
文件,第二参数env为{ mode: 'development', command: 'serve', ssrBuild: false }
,如果开发环境是development
,这里只是解构出了command
指令。- 再来理解
config.resolve
,这个不就是配置别名吗?alias
属性就是别名控制,后面都属于业务逻辑了。 - 所以自己编写一个插件的思路就是:
- 导出一个函数,返回一个对象。
- 选取合适的生命周期钩子(与配置相关),注入
config
和env
。 - 可以通过
apply
来应用插件的执行环境。
插件执行顺序
一个 Vite
插件可以额外指定一个 enforce
属性(类似于 webpack
加载器)来调整它的应用顺序。enforce
的值可以是pre
或 post
。解析后的插件将按照以下顺序排列:
Alias
- 带有
enforce: 'pre'
的用户插件 Vite
核心插件- 没有
enforce
值的用户插件 Vite
构建用的插件- 带有
enforce: 'post'
的用户插件 Vite
后置构建插件(最小化,manifest
,报告)
小技巧
这里在源码中看到了一个还不错的工具函数,与大家分享一下。
const fg = require('fast-glob')
/**
* Return all folders from the project directory
* @param options
*/
async function getDirectories(config) {
const { dir = 'src', root = process.cwd(), deep = true, depth = 1 } = config
const directories = await fg.sync(deep ? `${dir}/**/*` : `${dir}/*`, {
ignore: ['node_modules'], // 忽略
onlyDirectories: true, // 只读出文件夹
cwd: root, // 需要检测的路径
deep: depth, // 嵌套层级
absolute: true // 相对、绝对路径
})
if (!directories.length) {
// 给出警告,我简化成console.log
console.log('No Directories could be found!')
}
return directories
}
const config = {
dir: 'src',
root: process.cwd(),
deep: true,
depth: 2
}
getDirectories(config).then((list) => {
console.log(list)
})
html的处理
在webpack
中,我们用webpack-html-plugin
去生成我们的html
,在vite
中这一步其实内部已经帮你做了,但是关于html
文件的内容控制,vite
页提供了一个插件:vite-plugin-html
。如何去控制html
内容的呢?这需要ejs
来帮助我们。
ejs基础语法
<%= name %>
<%= age %>
<%= injectScript %>
<% if (user) { %>
<h2><%= user.name %></h2>
<% } %>
vite-plugin-html
使用
// index.html
<title>
<%= title %>
</title>
// vite-base-config.js
import {createHtmlPlugin} from 'vite-plugin-html';
plugins:[
createHtmlPlugin({
inject:{ // 注入data
data:{
title:"hello world" // title变量
injectScript: `<script src="./inject.js"></script>`,
}
}
});
]
vite-plugin-html
的原理简述:- 就是利用
transformIndexHtml
钩子函数对html
的转换,之后进行ejs模板替换成注入的变量,这样的一个好处是扩展性强。
- 就是利用
mock数据相关
在前后端排期的时候,接口文档永远比接口先出来,嗯?这不是废话吗?那在后端写接口的时候,前端在干啥啊,那当然是造组件,mock
数据咯,于是他来了。
vite
提供了vite-plugin-mock
来帮助你在项目中启动一个mock
服务器来模拟真实接口,等真实接口出来,直接替换,提高效率以便于联调。
vite-plugin-mock
的使用。
// vite.base.config.js
import {viteMockServe} from 'vite-plugin-mock'
plugins:[
viteMockServe()
]
//新建mock.js文件写入
module.exports = [
{
method:"post",
url:"/api/user",
response:()=>{
return {
code:200,
message:"success",
data:data
}
}
},
{
method:"post",
url:"/api/info",
response:()=>{
return {
code:200,
message:"success",
data:info
}
}
}
]
引入 mockjs包来mock数据,于是乎就有了下面的数据
于是乎有了这个插件,我们就可以很完美的、无缝衔接的去调真实环境的api
啦。(其实这个插件本身默认只是适用于开发环境)👀 👀 👀
vite-plugin-mock
的原理简述:- 就是利用
configureServer
钩子函数,通过各种MiddleWare
处理,交给vite
本地开发服务器,做接口数据的处理的。
- 就是利用
总结
本文我们去使用了三个比较有代表性的插件,了解了三个生命周期钩子:config
、transformIndexHtml
、configureServer
,更多钩子介绍,请戳 >>> vite生命周期函数
config(config, env)
:config
为UserConfig
,env
为指令,所以我们可以在这个钩子中去做一些关于配置项合并的事情。transformIndexHtml(html)
:html
为传入的index.html
,在这里我们可以去做一些关于还html内容的处理。configureServer(server)
:server
表示服务器的配置,这里我们可以用这个区做一些服务器相关的处理,比如数据mock
。
当然还有很多钩子,也只有一起去慢慢探索啦 ~ 🌶 ,下一章 >>> 前端构建工具vite进阶系列(五) -- vite的热更新(HMR)机制的实践与原理