Vite 构建工具笔记
第一章:走进 Vite 世界
一、Vite 是什么
Vite 是一个由 Vue.js 的作者 Evan You 开发的现代前端构建工具。它利用了浏览器原生的 ES modules 支持来实现快速的冷启动(Vite 使用原生 ES 模块 ESM 进行开发,只有当模块被实际请求时才会编译,这使得启动速度非常快)和即时热更新,提供了一个更快速、更轻量的开发环境。
前端构建工具是什么?
前端构建工具是用于自动化前端开发流程的工具,包括但不限于代码转换(例如,将 TypeScript 转换为 JavaScript、将 ES6+ 代码转换为 ES5 代码),文件合并,文件压缩,代码检查,测试等。
Vite 不会直接把 weakpack 干翻。侧重点不一样,webpack 更多的关注兼容性,Vite 关注浏览器端的开发体验。
Vite 的上手难度更低,而 webpack 的配置是非常多(loader, plugin……)且繁琐的。
二、原理
1. 依赖预构建
1)是什么
当首次启动 Vite 时,Vite 在本地加载你的站点之前预构建了项目依赖。默认情况下,它是自动且透明地完成的。这就是 Vite 执行时所做的“依赖预构建”。这个过程有三个目的:
CommonJS 和 UMD 兼容性。
性能:为了提高后续页面的加载性能,Vite 将那些具有许多内部模块的 ESM (ECMAScript Modules) 依赖项转换为单个模块。
方便对路径重写:对路径的处理上可以直接使用 .
vite/deps(这也是原生 esmodule 规范不敢支持 node_modules 的原因之一)。jsimport _ from "/node_modules/.vite/lodash"; // lodash可能也import了其他的东西 import __vite__cjsImport0_lodash from "/node_modules/.vite/deps/lodash.js?v=ebe57916";
注意:依赖预构建仅适用于开发模式,并使用 esbuild 将依赖项转换为 ES 模块。在生产构建中,将使用
@rollup/plugin-commonjs。
2)构建好的依赖放哪
[1] 文件系统缓存
Vite 将预构建的依赖项缓存到 node_modules/.vite 中。它会基于以下几个来源来决定是否需要重新运行预构建步骤:
- 包管理器的锁文件内容,例如
package-lock.json、yarn.lock、pnpm-lock.yaml,或者bun.lockb; - 补丁文件夹的修改时间;
vite.config.js中的相关字段;NODE_ENV的值。
只有在上述其中一项发生更改时,才需要重新运行预构建。
如果出于某些原因想要强制 Vite 重新构建依赖项,可以在启动开发服务器时指定 --force 选项,或手动删除 node_modules/.vite 缓存目录。
[2] 浏览器缓存
已预构建的依赖请求使用 HTTP 头 max-age=31536000, immutable 进行强缓存,以提高开发期间页面重新加载的性能。一旦被缓存,这些请求将永远不会再次访问开发服务器。如果安装了不同版本的依赖项(这反映在包管理器的 lockfile 中),则会通过附加版本查询自动失效。如果想通过本地编辑来调试依赖项,可以:
- 通过浏览器开发工具的 Network 选项卡暂时禁用缓存;
- 重启 Vite 开发服务器指定
--force选项,来重新构建依赖项; - 重新载入页面。
第二章:基础
一、配置 Vite
官方文档:配置 Vite
1. 配置文件
当以命令行方式运行 Vite 时,Vite 会自动解析项目根目录下名为 vite.config.js 的配置文件(也支持其他 ts、mjs 扩展名)。
最基础的配置文件是这样的:
// vite.config.js
export default {
// 配置选项
}注意:即使项目没有在
package.json中开启type: "module",Vite 也支持在配置文件中使用 ESM 语法。
可以显式地通过 --config 命令行选项指定一个配置文件(相对于 cwd (current working directory) 路径进行解析)。
vite --config my-config.js2. 配置智能提示
因为 Vite 本身附带 TypeScript 类型,所以可以通过 IDE 和 jsdoc 的配合来实现智能提示:
/** @type {import('vite').UserConfig} */
export default {
// ...
}另外可以使用 defineConfig 工具函数,这样不用 jsdoc 注解也可以获取类型提示:
import { defineConfig } from 'vite'
export default defineConfig({
// ...
})Vite 也直接支持 TypeScript 配置文件。可以在 vite.config.ts 中使用上述的 defineConfig 工具函数,或者 satisfies 运算符:
import type { UserConfig } from 'vite'
export default {
// ...
} satisfies UserConfig3. 情景配置
如果配置文件需要基于(dev / serve 或 build)命令或者不同的模式来决定选项,则可以选择导出这样一个函数:
export default defineConfig(({ command }) => {
if (command === 'serve') {
return {
// dev 独有配置
}
} else {
// command === 'build'
return {
// build 独有配置
}
}
})需要注意的是,在 Vite 的 API 中,在开发环境下 command 的值为 serve(在 CLI 中, vite dev 和 vite serve 是 vite 的别名),而在生产环境下为 build(vite build)。
二、环境变量和模式
官方文档:环境变量和模式
1. 环境变量
[1] 读取
Vite 使用 dotenv 从环境目录(envDir)中的下列文件加载额外的环境变量。
.env # 所有情况下都会加载
.env.local # 所有情况下都会加载,但会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略上面的环境变量配置文件这么多,以哪个为准?环境加载优先级
一份用于指定模式的文件(例如
.env.production)会比通用形式的优先级更高(例如.env)。Vite 执行时已经存在的环境变量有最高的优先级,不会被
.env类文件覆盖。例如当运行VITE_SOME_KEY=123 vite build的时候。
注意:.env 类文件会在 Vite 启动一开始时被加载,而改动会在重启服务器后生效。
在
vite.config.js文件中可以通过使用 vite 提供的 js api 的loadEnv函数来加载envDir中的.env文件。默认情况下只有前缀为VITE_会被加载,除非更改了prefixes配置。tsxfunction loadEnv( mode: string, envDir: string, prefixes: string | string[] = 'VITE_', ): Record<string, string>
[2] 代码中使用
js 中使用
Vite 在一个特殊的 import.meta.env 对象上暴露环境变量。加载的环境变量也会通过 import.meta.env 以字符串形式暴露给客户端源码,因此使用前要考虑转换为所需要的类型。
为了防止意外地将一些环境变量泄漏到客户端,只有以 VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码。例如下面这些环境变量:
VITE_SOME_KEY=123
DB_PASSWORD=foobar只有 VITE_SOME_KEY 会被暴露为 import.meta.env.VITE_SOME_KEY 提供给客户端源码,而 DB_PASSWORD 则不会。
console.log(import.meta.env.VITE_SOME_KEY) // "123"
console.log(import.meta.env.DB_PASSWORD) // undefined如果想自定义 env 变量的前缀,请参阅 envPrefix。
HTML 中使用
import.meta.env 中的任何属性都可以通过 %ENV_NAME% 语法在 HTML 文件中使用:
<h1>Vite is running in %MODE%</h1>
<p>Using data from %VITE_API_URL%</p>如果环境变量在 import.meta.env 中不存在,比如不存在的 %NON_EXISTENT%,则会将被忽略而不被替换,这与在 JS 中使用 import.meta.env.NON_EXISTENT 不同,JS 中会被替换为 undefined。
2. 模式
默认情况下,开发服务器 (dev 命令) 运行在 development (开发) 模式,而 build 命令则运行在 production (生产) 模式。
这意味着当执行 vite build 时,它会自动加载 .env.production 中可能存在的环境变量:
# .env.production
VITE_APP_TITLE=My App若想在 vite build 时来使用不同的环境变量配置文件,可以通过传递 --mode 选项标志来覆盖命令使用的默认模式。例如,如果想在 staging (预发布)模式下构建应用:
vite build --mode staging还需要新建一个 .env.staging 文件:
# .env.staging
VITE_APP_TITLE=My App (staging)三、HMR
1. HMR 简介
HMR 的全称叫做 Hot Module Replacement,即模块热替换或者模块热更新。在计算机领域当中也有一个类似的概念叫热插拔,HMR 的作用其实一样,就是在页面模块更新的时候,直接把页面中发生变化的模块替换为新的模块,同时不会影响其它模块的正常运作。
HMR = 局部刷新 + 状态保存
2. HMR API
Vite 作为完整的构建工具,基于原生 ESM 模块规范实现了 HMR 系统,能在文件变更时侦测并局部更新。其 HMR API 遵循由 Vite、Snowpack、WMR 共同制定的通用 ESM HMR 规范。
直观地来看一看 HMR API 的类型定义:
interface ImportMeta {
readonly hot?: {
readonly data: any
accept(): void
accept(cb: (mod: any) => void): void
accept(dep: string, cb: (mod: any) => void): void
accept(deps: string[], cb: (mods: any[]) => void): void
prune(cb: () => void): void
dispose(cb: (data: any) => void): void
decline(): void
invalidate(): void
on(event: string, cb: (...args: any[]) => void): void
}
}import.meta 对象为现代浏览器原生的一个内置对象,Vite 所做的事情就是在这个对象上的 hot 属性中定义了一套完整的属性和方法。因此,在 Vite 当中,你就可以通过 import.meta.hot 来访问关于 HMR 的这些属性和方法,比如 import.meta.hot.accept()。
1)模块更新时逻辑
import.meta.hot 的 accept 方法用于确定 Vite 热更新的边界,即接受模块更新。它有三种用法:接受自身、某个子模块或多个子模块的更新。
接受自身更新
当模块接受自身的更新时,则当前模块会被认为 HMR 的边界。也就是说,除了当前模块,其他的模块均未受到任何影响。

// render.ts
if (import.meta.hot) {
import.meta.hot.accept((mod) => mod.render())
}接受依赖模块的更新

// main.ts
import { render } from './render';
import './state';
render();
if (import.meta.hot) {
import.meta.hot.accept('./render.ts', (newModule) => {
newModule.render();
})
}接受多个子模块的更新

// main.ts
import { render } from './render';
import { initState } from './state';
render();
initState();
if (import.meta.hot) {
import.meta.hot.accept(['./render.ts', './state.ts'], (modules) => {
console.log(modules);
})
}发现打印的 modules 值是数组。

// main.ts
import { render } from './render';
import { initState } from './state';
render();
initState();
if (import.meta.hot) {
import.meta.hot.accept(['./render.ts', './state.ts'], (modules) => {
// 自定义更新
const [renderModule, stateModule] = modules;
if (renderModule) {
renderModule.render();
}
if (stateModule) {
stateModule.initState();
}
})
}2)模块销毁时逻辑
import.meta.hot.dispose:在模块更新时,旧模块需要销毁时需要做的一些事情。
// state.ts
let timer: number | undefined;
if (import.meta.hot) {
import.meta.hot.dispose(() => {
if (timer) {
clearInterval(timer);
}
})
}
export function initState() {
let count = 0;
timer = setInterval(() => {
let countEle = document.getElementById('count');
countEle!.innerText = ++count + '';
}, 1000);
}3)共享数据
hot.data 属性:在不同的模块实例间共享一些数据。
let timer: number | undefined;
if (import.meta.hot) {
// 初始化 count
if (!import.meta.hot.data.count) {
import.meta.hot.data.count = 0;
}
import.meta.hot.dispose(() => {
if (timer) {
clearInterval(timer);
}
})
}
export function initState() {
const getAndIncCount = () => {
const data = import.meta.hot?.data || {
count: 0
};
data.count = data.count + 1;
return data.count;
};
timer = setInterval(() => {
let countEle = document.getElementById('count');
countEle!.innerText = getAndIncCount() + '';
}, 1000);
}4)其它方法
1. import.meta.hot.decline()
这个方法调用之后,相当于表示此模块不可热更新,当模块更新时会强制进行页面刷新。
2. import.meta.hot.invalidate()
这个方法就更简单了,只是用来强制刷新页面。
3. 自定义事件
你还可以通过 import.meta.hot.on 来监听 HMR 的自定义事件,内部有这么几个事件会自动触发:
vite:beforeUpdate当模块更新时触发;vite:beforeFullReload当即将重新刷新页面时触发;vite:beforePrune当不再需要的模块即将被剔除时触发;vite:error当发生错误时(例如,语法错误)触发。
如果你想自定义事件可以通过上节中提到的 handleHotUpdate 这个插件 Hook 来进行触发:
// 插件 Hook
handleHotUpdate({ server }) {
server.ws.send({
type: 'custom',
event: 'custom-update',
data: {}
})
return []
}
// 前端代码
import.meta.hot.on('custom-update', (data) => {
// 自定义更新逻辑
})四、代码分割
一些名词:
- bundle 指的是整体的打包产物,包含 JS 和各种静态资源。
- chunk 指的是打包后的 JS 文件,是 bundle 的子集。
- vendor 是指第三方包的打包产物,是一种特殊的 chunk。
1. Vite 默认拆包策略
在生产环境下 Vite 完全利用 Rollup 进行构建,因此拆包也是基于 Rollup 来完成的,但 Rollup 本身是一个专注 JS 库打包的工具,对应用构建的能力还尚为欠缺,Vite 正好是补足了 Rollup 应用构建的能力,在拆包能力这一块的扩展就是很好的体现。
.
├── assets
│ ├── Dynamic.3df51f7a.js // Async Chunk
│ ├── Dynamic.f2cbf023.css // Async Chunk (CSS)
│ ├── favicon.17e50649.svg // 静态资源
│ ├── index.1e236845.css // Initial Chunk (CSS)
│ ├── index.6773c114.js // Initial Chunk
│ └── vendor.ab4b9e1f.js // 第三方包产物 Chunk
└── index.html // 入口 HTML一方面 Vite 实现了自动 CSS 代码分割的能力。而另一方面, Vite 基于 Rollup 的 manualChunks API 实现了应用拆包的策略:
- 对于
Initital Chunk而言,业务代码和第三方包代码分别打包为单独的 chunk,在上述的例子中分别对应index.js和vendor.js。需要说明的是,这是 Vite 2.9 版本之前的做法,而在 Vite 2.9 及以后的版本,默认打包策略更加简单粗暴,将所有的 js 代码全部打包到index.js中。 - 对于
Async Chunk而言 ,动态 import 的代码会被拆分成单独的 chunk,如上述的 Dynacmic 组件。
Vite 默认拆包的优势在于实现了 CSS 代码分割与业务代码、第三方库代码、动态 import 模块代码三者的分离,但缺点也比较直观,第三方库的打包产物容易变得比较臃肿,上述例子中的 vendor.js 的大小已经达到 500 KB 以上,显然是有进一步拆包的优化空间的,这个时候我们就需要用到 Rollup 中的拆包 API —— manualChunks 了。
2. 自定义拆包策略
针对更细粒度的拆包,Vite 的底层打包引擎 Rollup 提供了 manualChunks,让我们能自定义拆包策略。
// vite.config.ts
export default {
build: {
rollupOptions: {
output: {
// manualChunks 配置
manualChunks: {},
},
}
},
}manualChunks 配置为对象
key 代表 chunk 的名称, value 为一个字符串数组,每一项为第三方包的包名。
// vite.config.ts
{
build: {
rollupOptions: {
output: {
// manualChunks 配置
manualChunks: {
// 将 React 相关库打包成单独的 chunk 中
'react-vendor': ['react', 'react-dom'],
// 将 Lodash 库的代码单独打包
'lodash': ['lodash-es'],
// 将组件库的代码打包
'library': ['antd', '@arco-design/web-react'],
},
},
}
},
}Vite 中的默认拆包策略是通过函数的方式来进行配置的。
ts// Vite 部分源码 function createMoveToVendorChunkFn(config: ResolvedConfig): GetManualChunk { const cache = new Map<string, boolean>() // 返回值为 manualChunks 的配置 return (id, { getModuleInfo }) => { // Vite 默认的配置逻辑其实很简单 // 主要是为了把 Initial Chunk 中的第三方包代码单独打包成`vendor.[hash].js` if ( id.includes('node_modules') && !isCSSRequest(id) && // 判断是否为 Initial Chunk staticImportedByEntry(id, getModuleInfo, cache) ) { return 'vendor' } } }
manualChunk 配置为函数
Rollup 会对每一个模块调用 manualChunks 函数,在 manualChunks 的函数入参中你可以拿到模块 id 及模块详情信息 ,经过一定的处理后返回 chunk 文件的名称 ,这样当前 id 代表的模块便会打包到你所指定的 chunk 文件中。
// vite.config.ts
manualChunks(id) {
if (id.includes('antd') || id.includes('@arco-design/web-react')) {
return 'library';
}
if (id.includes('lodash')) {
return 'lodash';
}
if (id.includes('react')) {
return 'react';
}
}3. 终极解决方案
安装插件
pnpm i vite-plugin-chunk-split -D然后在项目中引入并使用。
// vite.config.ts
import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
export default {
chunkSplitPlugin({
// 指定拆包策略
customSplitting: {
// 1. 支持填包名。`react` 和 `react-dom` 会被打包到一个名为`render-vendor`的 chunk 里面 (包括它们的依赖,如 object-assign)
'react-vendor': ['react', 'react-dom'],
// 2. 支持填正则表达式。src 中 components 和 utils 下的所有文件被会被打包为`component-util`的 chunk 中
'components-util': [/src\/components/, /src\/utils/]
}
})
}具体用法参见:vite-plugin-chunk-split README
此外还有 vite-plugin-dynamic-chunk 插件也是如此。
第三章:功能
1. CSS
vite 天生就支持对 css 文件的直接处理。导入 .css 文件后 vite 会干如下事情。
- vite 在读取到 main.js 中引用到了 Index.css。
- 直接去使用 fs 模块去读取 index.css 中文件内容。
- 直接创建一个 style 标签,将 index.css 中文件内容直接 copy 进 style 标签里。
- 将 style 标签插入到 index.html 的 head 中。
- 将该 css 文件中的内容直接替换为 js 脚本(方便 HMR 热更新或者 css 模块化)。同时设置 Content-Type 为 js,从而让浏览器以 JS 脚本的形式来执行该 css 后缀的文件。
1)CSS Modules
任何以 .module.css 为后缀名的 CSS 文件都被认为是一个 CSS modules 文件。导入这样的文件会返回一个相应的模块对象:
/* example.module.css */
.red {
color: red;
}import classes from './example.module.css'
document.getElementById('foo').className = classes.red原理:全部都是基于 node
- module.css (module 是一种约定,表示需要开启 css 模块化)
- 他会将你的所有类名进行一定规则的替换(将 footer 替换成 _footer_i22st_1)
- 同时创建一个映射对象
- 将替换过后的内容塞进 style 标签里,然后放入到 head 标签中 (能够读到 index.html 的文件内容)
- 将 componentA.module.css 内容进行全部抹除,替换成 JS 脚本
- 将创建的映射对象在脚本中进行默认导出
2)CSS 预处理器
Vite 同时提供了对 .scss, .sass, .less, .styl 和 .stylus 文件的内置支持。没有必要为它们安装特定的 Vite 插件,但必须安装相应的预处理器依赖:
# .scss and .sass
npm add -D sass
# .less
npm add -D less
# .styl and .stylus
npm add -D stylus如果使用的是单文件组件,可以通过 <style lang="sass">(或其他预处理器)自动开启对代码的处理。
3)共享选项
[1] css.modules
CSS modules 行为可以通过 css.modules 选项 进行配置。
localsConvention
用于配置 CSS 模块的类名转换规则。
localsConvention?:
| 'camelCase'
| 'camelCaseOnly'
| 'dashes'
| 'dashesOnly'
| ((
originalClassName: string,
generatedClassName: string,
inputFile: string,
) => string)CSS 模块允许在 JavaScript 中以对象的形式导入 CSS 类。localsConvention 选项决定了这些类名在导入时应该如何转换。
localsConvention 选项有四个可能的值:
'camelCase':在这种模式下,类名会被转换为驼峰式(camelCase)和短横线式(dashes)两种形式。例如,.my-class会被转换为{ 'my-class': '...', myClass: '...' }。'camelCaseOnly':在这种模式下,类名只会被转换为驼峰式。例如,.my-class会被转换为{ myClass: '...' }。'dashes':在这种模式下,类名会被转换为驼峰式和短横线式两种形式,但是驼峰式的类名会被转换为短横线式。例如,.myClass会被转换为{ 'my-class': '...', myClass: '...' }。'dashesOnly':在这种模式下,类名只会被转换为短横线式。例如,.myClass会被转换为{ 'my-class': '...' }。
如果 css.modules.localsConvention 设置开启了 camelCase 格式变量名转换(例如 localsConvention: 'camelCaseOnly'),可以使用按名导入。
// .apply-color -> applyColor
import { applyColor } from './example.module.css'
document.getElementById('foo').className = applyColorscopeBehaviour
scopeBehaviour?: 'global' | 'local'local 表示在 CSS 选择器后面加上 hash 来防止类名冲突;global 表示不会在 CSS 选择器后面加上 hash,原来的选择器名字是什么,就是什么。
generateScopedName
用于自定义 CSS 模块的作用域名生成规则。
generateScopedName?:
| string
| ((name: string, filename: string, css: string) => string)默认情况下,Vite 会为 CSS 模块生成唯一的作用域名,以避免样式冲突。generateScopedName 选项允许你自定义这个生成规则。
generateScopedName 选项可以是一个字符串或一个函数:
如果它是一个字符串,那么这个字符串应该是一个包含
[name]、[filename]、[hash]等占位符的模板。例如,你可以设置generateScopedName: '[name]__[hash]',那么生成的作用域名可能是myClass__3a4b2c。如果它是一个函数,那么这个函数应该接受三个参数:
name(原始的类名)、filename(CSS 文件的路径)、css(CSS 文件的内容),并返回一个字符串作为作用域名。例如,你可以设置generateScopedName: (name, filename, css) => name + '__' + hash(css),那么生成的作用域名可能是myClass__3a4b2c。在这个例子中,
myClass是一个 CSS 类,3a4b2c是这个 CSS 文件内容的哈希值。这样,每个 CSS 类都会有一个唯一的作用域名,避免了样式冲突。
hashPrefix
用于自定义 CSS 模块的哈希前缀。
hashPrefix?: string在 CSS 模块中,为了避免样式冲突,Vite 会为每个类名生成一个唯一的哈希值。hashPrefix 选项允许你为这个哈希值添加一个前缀。
例如,如果你设置 hashPrefix: 'my-prefix',那么生成的类名可能是 my-prefix__myClass__3a4b2c。
在这个例子中,myClass 是一个 CSS 类,3a4b2c 是这个 CSS 类的哈希值,my-prefix 是你自定义的前缀。
这个选项可以帮助你在查看生成的 CSS 代码时更容易识别出来自哪个模块的样式。
globalModulePaths
用于指定哪些 CSS 模块应该被视为全局模块。
globalModulePaths?: RegExp[]默认情况下,Vite 会将 .module.css 文件视为 CSS 模块,而将其他 CSS 文件视为全局 CSS 文件。全局 CSS 文件中的样式会应用到全局,而不是仅限于特定的模块。
globalModulePaths 选项允许你改变这个行为。这个选项应该是一个正则表达式数组,这些正则表达式用于匹配文件路径。匹配的文件会被视为全局 CSS 文件,即使它们的扩展名是 .module.css。
例如,如果设置 globalModulePaths: [/global\.css$/],那么所有以 global.css 结尾的文件都会被视为全局 CSS 文件,即使它们的扩展名是 .module.css。
这个选项可以帮助你更灵活地管理你的 CSS 模块和全局样式。
[2] css.preprocessorOptions
2. 静态资源处理
四、使用插件
1. vite-aliases
1)基本使用
vite-aliases 是一个用于自动生成 Vite 别名的插件。这些别名可以让你更方便地引用项目中的文件。
首先,需要安装 vite-aliases 插件。可以通过 npm 或 yarn 来安装:
npm install vite-aliases --save-dev
# 或者
yarn add vite-aliases --dev然后,在 Vite 配置文件(默认是 vite.config.js 或 vite.config.ts)中引入并使用这个插件:
import { defineConfig } from 'vite'
import viteAliases from 'vite-aliases'
export default defineConfig({
plugins: [viteAliases()]
})现在,vite-aliases 插件会自动为你的 src 目录生成别名。例如,如果有一个位于 src/components/MyComponent.vue 的文件,可以通过以下方式来引用它:
import MyComponent from '@/components/MyComponent.vue'在这个例子中,@ 是一个别名,代表 src 目录。
也可以通过传递一个选项对象来自定义 vite-aliases 插件的行为。例如,可以更改别名或添加额外的路径:
export default defineConfig({
plugins: [viteAliases({
dir: 'src', // 设置要为其生成别名的目录
prefix: '@', // 设置别名的前缀
deep: true, // 如果为 true,则为所有子目录生成别名。如果为 false,则只为顶级目录生成别名
allowGlobalAlias: false, // 如果为 true,则允许全局别名。全局别名是指不以任何前缀开头的别名
useTypescript: false, // 如果为 true,则从 TypeScript 配置文件(tsconfig.json)中读取 paths 选项,并将其用作别名
useConfig: false, // 如果为 true,则从 Vite 配置文件中读取 resolve.alias 选项,并将其用作别名
useFindConfig: false, // 如果为 true,则自动查找并使用 Vite 和 TypeScript 配置文件中的别名
extensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'vue', 'mjs'] // 设置要为其生成别名的文件类型
})]
})更多详细的配置选项,你可以查看 vite-aliases 的 GitHub 仓库。
2)编写 vite-aliases 插件
[1] 必要知识
Vite 独有钩子
config
config 是一个插件钩子,它允许你修改或增强 Vite 的配置。这个钩子在 Vite 解析和合并配置后,但在任何其他钩子运行之前调用。
config 钩子接收一个参数,这个参数是一个包含 config 和 env 属性的对象。config 属性是解析和合并后的 Vite 配置,env 属性是一个包含环境信息的对象,例如 mode(当前的模式,例如 development 或 production)、command(当前的命令,例如 serve 或 build)等。
config 钩子必须返回一个对象,这个对象的属性将被合并到 Vite 配置中。你可以返回一个部分配置来修改 Vite 的配置,也可以返回 null 或 undefined 来不做任何修改。
[2] 编写代码
// vite的插件必须返回给vite一个配置对象
const fs = require("fs");
const path = require("path");
function diffDirAndFile(dirFilesArr = [], basePath = "") {
const result = {
dirs: [],
files: []
}
dirFilesArr.forEach(name => {
// 我直接用异步的方式去写的
const currentFileStat = fs.statSync(path.resolve(__dirname, basePath + "/" + name));
console.log("current file stat", name, currentFileStat.isDirectory());
const isDirectory = currentFileStat.isDirectory();
if (isDirectory) {
result.dirs.push(name);
} else {
result.files.push(name);
}
})
return result;
}
function getTotalSrcDir(keyName) {
const result = fs.readdirSync(path.resolve(__dirname, "../src"));
const diffResult = diffDirAndFile(result, "../src");
console.log("diffResult", diffResult);
const resolveAliasesObj = {}; // 放的就是一个一个的别名配置 @assets: xxx
diffResult.dirs.forEach(dirName => {
const key = `${keyName}${dirName}`;
const absPath = path.resolve(__dirname, "../src" + "/" + dirName);
resolveAliasesObj[key] = absPath;
})
return resolveAliasesObj;
}
module.exports = ({
keyName = "@"
} = {}) => {
return {
config(config, env) {
// 只是传给你 有没有执行配置文件: 没有
console.log("config", config, env);
// config: 目前的一个配置对象
// production development serve build yarn dev yarn build
// env: mode: string, command: string
// config函数可以返回一个对象, 这个对象是部分的viteconfig配置【其实就是你想改的那一部分】
const resolveAliasesObj = getTotalSrcDir(keyName);
console.log("resolve", resolveAliasesObj);
return {
// 在这我们要返回一个resolve出去, 将src目录下的所有文件夹进行别名控制
// 读目录
resolve: {
alias: resolveAliasesObj
}
};
}
}
}2. vite-plugin-mock
1)基本使用
vite-plugin-mock 是一个用于 Vite 的模拟数据插件,可以帮助在开发过程中快速创建模拟 API。
首先,需要安装 vite-plugin-mock 插件。可以通过 npm 或 yarn 来安装:
npm install vite-plugin-mock --save-dev
# 或者
yarn add vite-plugin-mock --dev然后,在 Vite 配置文件(默认是 vite.config.js 或 vite.config.ts)中引入并使用这个插件:
import { defineConfig } from 'vite'
import { viteMockServe } from 'vite-plugin-mock'
export default defineConfig({
plugins: [viteMockServe()]
})现在,可以在项目中创建模拟数据文件。默认情况下,vite-plugin-mock 会加载 mock 目录下的所有 .ts 文件。例如,可以在项目根目录下创建一个 mock/user.ts 文件:
export default [
{
url: '/api/user',
method: 'get',
response: () => {
return {
code: 0,
data: {
name: 'vben',
},
}
},
},
]在这个例子中,当访问 /api/user 时,服务器会返回一个 JSON 对象,这个对象包含一个 code 属性和一个 data 属性。
也可以通过传递一个选项对象来自定义 vite-plugin-mock 插件的行为。例如,可以更改模拟数据文件的目录或启用插件的热更新功能:
export default defineConfig({
plugins: [viteMockServe({
supportTs: false,
mockPath: 'mock',
localEnabled: process.env.NODE_ENV === 'development',
prodEnabled: process.env.NODE_ENV === 'production',
injectCode: `
import { setupProdMockServer } from '../mock/_createProductionServer';
setupProdMockServer();
`
})]
})更多详细的配置选项,你可以查看 vite-plugin-mock 的 GitHub 仓库。
2)生成数据
vite-plugin-mock 插件可以配合数据生成库(如 faker.js 或 mockjs)来模拟生成数据。
以下是一个使用 mockjs 的例子:
首先,你需要安装 mockjs:
npm install mockjs --save-dev
# 或者
yarn add mockjs --dev然后,在你的模拟数据文件中,你可以使用 mockjs 来生成随机数据:
import Mock from 'mockjs'
export default [
{
url: '/api/user',
method: 'get',
response: () => {
return Mock.mock({
'code': 0,
'data|1-10': [{
'name': '@NAME',
'id|+1': 1,
'age|18-32': 1
}]
})
},
},
]在这个例子中,当你访问 /api/user 时,服务器会返回一个包含 1 到 10 个用户的数组,每个用户都有一个随机的名字、一个递增的 ID 和一个 18 到 32 之间的随机年龄。
同样的,你也可以使用 faker.js 来生成随机数据。首先安装 faker.js:
npm install faker --save-dev
# 或者
yarn add faker --dev然后在你的模拟数据文件中使用 faker.js:
import faker from 'faker'
export default [
{
url: '/api/user',
method: 'get',
response: () => {
return {
code: 0,
data: {
name: faker.name.findName(),
email: faker.internet.email(),
},
}
},
},
]在这个例子中,当你访问 /api/user 时,服务器会返回一个包含随机生成的名字和电子邮件的用户。

