本文目录
Why Vite
前提:
- 浏览器只能理解原生的代码,无法理解各类现代前端框架的语法
- 随着现代应用体积的增大,老一代构建工具webpack/Rollup/Parcel
- 开发服务器启动非常缓慢,vite采用了现代语言工具打破JS性能瓶颈,不再处理ESM打包将工作流简化
- HMR非常缓慢,vite在ESM上执行HMR,同时配合浏览器对结果进行缓存,让HMR速度飞快
- 生产环境为了应用最终加载性能,仍然采用Rollup进行打包
- 作为构建工具,从源代码得出,浏览器可理解,高性能的代码
- 开发时体验优化,修改文件时热更新,快,SourceMap
- 部署时体验优化,打包结果要很小,删掉没用的代码,类型检查
开发环境和部署环境的差异,也就是常见的两个不同的命令,dev和build的行为差异
Vite就是法语中快的意思,发明出来就是为了性能优化
- 开发服务器的架构设计,主要就是考虑大规模的项目依旧可以快速的HMR
- 为了保持快速和灵活性的平衡
- 对CPU重依赖的部分如转译,压缩,依赖解析等工作采用esbuild和SWC这样的,采用Go和Rust现代高性能语言的,现成的原生工具,自定义能力较差
- 而其他对速度影响小的操作,如插件系统,热更新机制,配置逻辑,由Vite自身决定,使用JavaScript编写,自定义程度高
- 部分esbuild无法编译的语法,Vite会选择性的使用babel补充
- 构建阶段,vite不使用esbuild而是采用Rollup进行打包,因为Build时速度不那么重要,而打包产物的体积大小更重要,这一点上Rollup做的更好
Vite的功能
Vite 主要由两部分组成:
- 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热替换(HMR)。
- 一套构建指令,它使用 Rolldown 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
也就是开发和构建两个大的阶段,开发阶段为开发体验服务,构建阶段打包源码为浏览器可直接消费的代码,并进行一系列性能优化
项目开发阶段优化开发体验的功能,大概可以总结为两点:
另一个功能维度是文件的转译构建,而构建阶段,就是把开发者友好的代码,转译为浏览器友好的代码的过程,但这里其实开发服务器也显然需要经过转译,只不过,不同的阶段开发/生产工作负载是不同的,对于浏览器友好的标准也是不同的
-
文件的转译(服务对象:开发者⇒浏览器)
Vite 的常见配置
配置的作用域:
- 共享选项,所有作用域都会使用的通用配置
- 服务器选项,用于开发服务器相关的配置
- 构建选项,用于配置构建行为
- 预览选项,用于配置预览行为
- 依赖优化选项,用于配置依赖
- SSR 选项,用于配置服务端渲染行为
- Worker 选项,用于配置web worker
export default defineConfig(() => {
return {
plugins: [...],
base:"/",
resolve: {
extensions: [...],
dedupe: [...],
alias: [...],
},
cacheDir: "node_modules/.vite-xxxx",
build: {
rollupOptions: {...},
},
server: {
port: 5177,
strictPort: true,
host: "0.0.0.0",
// Pre-transform requested modules more aggressively in dev.
preTransformRequests: true,
},
optimizeDeps: {
include: [...],
},
}
}
“作用域”先分清:plugins / resolve / cacheDir 是共享项,server 只管开发,build 只管生产构建,optimizeDeps 只管开发期依赖预打包。
-
defineConfig(() => …):函数式配置,方便按环境/命令动态返回配置。
-
plugins:Vite 插件链,负责代码转换、解析、HMR、开发服务器中间件等,顺序有影响。
-
resolve.extensions:导入时不写扩展名,Vite 按这个顺序去试。你们这里把 .ts/.tsx 放前面,是为了让“表面上像 .js 的导入”优先落到源码实现。
-
resolve.dedupe:强制这些依赖只解析到同一份实例,避免 monorepo/linked package 里出现两份 React 之类导致的上下文或 hook 问题。
-
resolve.alias:路径别名替换。@、app、api 这类是源码路径映射;@ffmpeg/* 那种是把包强行指到指定入口。
-
cacheDir:Vite 缓存目录,主要放依赖预构建结果。自定义目录通常是为了隔离不同配置/分支的缓存。
-
build.rollupOptions:构建时底层打包器参数。Vite 8 里它是 build.rolldownOptions 的别名/旧名,作用还是自定义输出、分包、外部依赖等。
-
server.port:开发服务器端口。
-
server.strictPort:端口被占用时直接失败,不自动换下一个端口。
-
server.host:监听地址;0.0.0.0 表示监听所有网卡,方便局域网/容器/WSL 访问。
-
server.preTransformRequests:它是让 dev 时更积极地预转译被请求的模块,减少冷启动瀑布。
- 为何要用:在大型 monorepo、含较大第三方包(如 pixi、ffmpeg.wasm)或按需加载场景,默认按需优化有时会导致第一次请求超时、504 或卡顿;开启此项可改善开发体验(更快拿到可执行模块)。
- 让 dev server 在响应模块请求前先把模块做一次预处理(transform),避免每次按需加载时触发较慢的按需优化路径。
如何编写插件
插件的生命周期:
Vite 插件的生命周期钩子(Hooks)可以分为两类:Vite 专属钩子和Rollup/Rolldown 通用构建钩子。
一、 Vite 插件的生命周期(执行顺序)
- 初始化阶段(服务启动或打包开始时)
config(config, env)(Vite 专属):在解析 Vite 配置之前调用。你可以用它来修改、覆盖或者深度合并用户的vite.config.js配置。configResolved(config)(Vite 专属):配置解析完成。此时你可以获取到最终的合并配置,通常用来把配置存到闭包里,供后续的钩子使用。options(options)(通用):获取 Rollup 的基础配置。buildStart(options)(通用):构建真正开始。通常在这里做一些初始化任务(比如启动一个子进程、预处理某些文件)。
- 开发服务器专属阶段(仅在
vite dev模式下)
configureServer(server)(Vite 专属):非常核心。配置开发服务器,你可以拿到 Vite 内部的 Connect 服务器实例,用于添加自定义的后端中间件(比如写 Mock 接口、代理请求等)。configurePreviewServer(server)(Vite 专属):类似于上一个,但是作用于vite preview启动的预览服务器。handleHotUpdate(ctx)(Vite 专属):当监控的文件发生变化时触发。你可以获取到变化的模块,并自定义热更新(HMR)逻辑。比如,如果是某个特殊的 JSON 变了,你不想让整个页面刷新,而是只派发一个自定义事件给客户端。
- 文件请求与处理阶段(核心流水线,每个文件都会经历)
当浏览器发起一个请求,或者打包工具解析到一个模块依赖时执行:
transformIndexHtml(html)(Vite 专属):仅针对入口 HTML 文件。可以在这里往 HTML 里动态注入<script>、<meta>或者替换内容。resolveId(source, importer)(通用):路径解析。告诉 Vite 这个模块名对应的真实物理路径是什么(比如把import 'vue'解析到node_modules/...)。也可以用来拦截和生成“虚拟模块”。load(id)(通用):加载文件。根据上一步解析出的真实路径,读取并返回文件的内容(代码字符串)。transform(code, id)(通用):最频繁使用的钩子。接收上一步返回的源码,进行转换处理(比如把 TypeScript 转成 JavaScript,把 Sass 转成 CSS)。
- 构建结束与生成产物阶段(主要在
vite build模式下)
buildEnd(error)(通用):Vite/Rollup 完成了所有模块的解析和构建。- (以下为 Rollup 专属的输出阶段钩子,Vite 打包时也会执行):
renderChunk:处理每个单独生成的 chunk(代码块)。generateBundle:在把最终代码写入磁盘前调用,可以在这里删除某些输出文件,或者修改文件名。writeBundle:文件已经完全写入磁盘。
closeBundle(通用):整个构建进程结束。
二、 Vite 插件的常见用途
“能不能在 Vite 打包的时候,顺便帮我做点什么”时,你就可以考虑写一个 Vite 插件,对照上面的生命周期,找到对应的钩子插进去即可,以下是 Vite 插件的常见用途:
- 框架与语法编译 (Code Transformation)
这是最基础的用途。浏览器只认识标准的 JS、CSS 和 HTML。
- 用途:把特定的语法(如 Vue 单文件组件、React JSX、Svelte、Markdown)编译成标准的 JavaScript。
- 代表插件:
@vitejs/plugin-vue,@vitejs/plugin-react。 - 使用钩子:主要使用
transform。
- 创建“虚拟模块” (Virtual Modules)
你可以在代码里 import 一个根本不存在的文件,由插件在构建时动态生成内容。
- 用途:
- 自动收集文件系统目录,生成路由(如
vite-plugin-pages)。 - 将构建时间、Git Commit Hash 等环境变量注入到代码中。
- 提取所有的 SVG 图标,合并成一个雪碧图注入到应用中(如
vite-plugin-svg-icons)。
- 自动收集文件系统目录,生成路由(如
- 使用钩子:结合使用
resolveId(拦截不存在的路径名)和load(返回动态生成的代码)。
- 开发服务器拦截与 Mock API (Mock & Dev Proxy)
- 用途:在前端开发阶段,后端接口还没准备好,前端需要模拟数据。可以通过插件直接在 Vite 开发服务器上拦截指定的请求,返回 JSON 假数据。
- 使用钩子:
configureServer。
- HTML 动态注入 (HTML Manipulation)
- 用途:
- 根据环境变量(开发/生产),在
<head>里注入不同的统计代码(如 Google Analytics、百度统计)。 - 做 SEO 优化,动态生成
<meta>标签。 - 压缩 HTML 缩小体积。
- 根据环境变量(开发/生产),在
- 代表插件:
vite-plugin-html。 - 使用钩子:
transformIndexHtml。
- 自动化构建增强 (Automated Workflow)
- 用途:
- 按需自动导入:再也不用手动写
import { ref, reactive } from 'vue',由插件在代码编译前自动帮你加上。 - 组件自动注册:UI 库组件按需引入。
- 按需自动导入:再也不用手动写
- 代表插件:
unplugin-auto-import,unplugin-vue-components。 - 使用钩子:
transform。
- 代码压缩与清理 (Optimization & Cleanup)
- 用途:
- 在生产环境打包时,自动清除代码里的
console.log或debugger。 - 对打包生成的图片进行无损压缩。
- 分析打包产物大小(生成包体可视化报告)。
- 在生产环境打包时,自动清除代码里的
- 使用钩子:
transform(清理代码),generateBundle或closeBundle(产物分析和图片压缩)。