Vite
Vite(法语中"快速"的意思,发音 /vit/
)是下一代前端构建工具的全面指南。
Vite 简介
Vite 是由 Vue.js 的创建者尤雨溪(Evan You)开发的现代前端构建工具。它为现代 Web 项目提供了更快、更精简的开发体验。Vite 主要由两部分组成:
- 一个开发服务器,通过原生 ES 模块提供源文件,具有丰富的内置功能和极快的热模块替换(HMR)。
- 一个构建命令,使用 Rollup 打包代码,预配置为输出高度优化的生产静态资源。
主要特点
- 极速冷启动:Vite 利用浏览器中的原生 ES 模块,在开发过程中避免打包。
- 即时热模块替换(HMR):更新会立即反映在浏览器中,无需重新加载页面。
- 真正的按需编译:只有正在编辑的模块才会被转换和更新。
- 开箱即用支持 TypeScript、JSX、CSS 预处理器等。
- 优化的构建:生产构建使用 Rollup 高度优化。
- 框架无关:适用于 Vue、React、Preact、Lit、Svelte 和原生 JavaScript。
- 插件生态系统:可通过 Rollup 兼容插件扩展。
Vite 入门
安装
您可以使用 npm、yarn 或 pnpm 创建新的 Vite 项目:
# npm
npm create vite@latest my-vite-app -- --template vanilla
# yarn
yarn create vite my-vite-app --template vanilla
# pnpm
pnpm create vite my-vite-app --template vanilla
可用的模板包括:
vanilla
:纯 JavaScriptvanilla-ts
:TypeScriptvue
:Vuevue-ts
:Vue + TypeScriptreact
:Reactreact-ts
:React + TypeScriptpreact
:Preactpreact-ts
:Preact + TypeScriptlit
:Litlit-ts
:Lit + TypeScriptsvelte
:Sveltesvelte-ts
:Svelte + TypeScript
项目结构
典型的 Vite 项目结构如下:
my-vite-app/
├── node_modules/
├── public/ # 静态资源,将按原样提供服务
│ └── favicon.ico
├── src/ # 应用程序源代码
│ ├── assets/ # 将由 Vite 处理的资源
│ │ └── logo.png
│ ├── components/ # 应用程序组件
│ │ └── HelloWorld.vue
│ ├── App.vue # 根组件(Vue 项目)
│ └── main.js # 应用程序入口点
├── index.html # HTML 入口点
├── package.json # 项目依赖和脚本
├── vite.config.js # Vite 配置
└── README.md # 项目文档
开发服务器
启动开发服务器:
# 导航到项目目录
cd my-vite-app
# 安装依赖
npm install
# 启动开发服务器
npm run dev
开发服务器默认将在 http://localhost:5173
启动。
核心概念
ES 模块
Vite 利用浏览器中的原生 ES 模块,这使得它能够在开发过程中无需打包即可提供源代码。这导致更快的启动时间和更高效的热模块替换。
<!-- index.html -->
<script type="module" src="/src/main.js"></script>
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
createApp(App).mount('#app')
热模块替换(HMR)
Vite 提供了一个 HMR API,允许模块交换和更新,而无需重新加载页面:
// 带有 HMR 支持的计数器模块
export let count = 0
export function increment() {
count++
}
// HMR 接口
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
count = newModule.count
})
}
静态资源处理
Vite 自动处理和优化静态资源:
// 导入资源
import imgUrl from './img.png' // 解析为公共 URL
import svgContent from './icon.svg?raw' // 原始内容作为字符串
import Component from './Component.vue?url' // 获取组件的 URL
// 在模板中使用资源(Vue 示例)
<img :src="imgUrl" alt="图片">
环境变量
Vite 在特殊的 import.meta.env
对象上暴露环境变量:
console.log(import.meta.env.MODE) // 'development' 或 'production'
console.log(import.meta.env.VITE_API_URL) // 以 VITE_ 为前缀的自定义变量
自定义环境变量可以在 .env
文件中定义:
# .env
VITE_API_URL=https://api.example.com
配置
基本配置
Vite 通过项目根目录中的 vite.config.js
文件进行配置:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
server: {
port: 3000,
open: true
},
build: {
outDir: 'dist',
minify: 'terser'
}
})
常见配置选项
插件
插件扩展 Vite 的功能:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
plugins: [
vue(),
vueJsx(),
// 自定义插件
{
name: 'my-plugin',
transform(code, id) {
if (id.endsWith('.special')) {
return { code: transformSpecialFile(code), map: null }
}
}
}
]
})
开发服务器
配置开发服务器:
export default defineConfig({
server: {
host: '0.0.0.0',
port: 3000,
strictPort: true,
https: true,
open: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
构建选项
配置生产构建:
export default defineConfig({
build: {
outDir: 'dist',
assetsDir: 'assets',
assetsInlineLimit: 4096, // 4kb
cssCodeSplit: true,
sourcemap: true,
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
},
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router'],
utils: ['./src/utils/index.js']
}
}
}
}
})
解析
配置模块解析:
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'~': path.resolve(__dirname, './node_modules')
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}
})
模式特定配置
您可以为开发和生产指定不同的配置:
export default defineConfig(({ command, mode }) => {
const isProduction = mode === 'production'
return {
define: {
__APP_VERSION__: JSON.stringify('1.0.0'),
__API_URL__: isProduction
? JSON.stringify('https://api.example.com')
: JSON.stringify('http://localhost:8080')
},
build: {
minify: isProduction ? 'terser' : false
}
}
})
高级功能
CSS 处理
Vite 内置支持 CSS 预处理:
// 导入 CSS
import './style.css'
// CSS 模块
import styles from './style.module.css'
element.className = styles.heading
// CSS 预处理器(需要安装相应的预处理器)
import './style.scss'
import './style.less'
import './style.styl'
在 vite.config.js
中配置 CSS:
export default defineConfig({
css: {
modules: {
scopeBehaviour: 'local',
localsConvention: 'camelCaseOnly'
},
preprocessorOptions: {
scss: {
additionalData: `@import "./src/styles/variables.scss";`
},
less: {
javascriptEnabled: true
}
},
postcss: {
plugins: [autoprefixer()]
}
}
})
TypeScript 集成
Vite 开箱即用支持 TypeScript:
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
interface User {
name: string
age: number
}
const user: User = {
name: 'John',
age: 30
}
console.log(user)
createApp(App).mount('#app')
在 vite.config.js
中配置 TypeScript:
export default defineConfig({
esbuild: {
target: 'es2020'
}
})
WebAssembly
直接导入 WebAssembly 模块:
import init, { add } from './add.wasm'
init().then(() => {
console.log(add(1, 2)) // 3
})
Web Workers
直接导入 Web Workers:
// 标准 worker
import Worker from './worker.js?worker'
const worker = new Worker()
// 共享 worker
import SharedWorker from './worker.js?sharedworker'
const sharedWorker = new SharedWorker()
// 内联 worker
import InlineWorker from './worker.js?worker&inline'
服务器端渲染(SSR)
Vite 提供对服务器端渲染的一流支持:
// vite.config.js
export default defineConfig({
ssr: {
// SSR 特定选项
external: ['some-external-dependency'],
noExternal: ['some-dependency-to-bundle']
}
})
基本 SSR 设置:
// server.js
import fs from 'fs'
import path from 'path'
import express from 'express'
import { createServer as createViteServer } from 'vite'
async function createServer() {
const app = express()
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom'
})
app.use(vite.middlewares)
app.use('*', async (req, res) => {
const url = req.originalUrl
try {
// 读取 index.html
let template = fs.readFileSync(
path.resolve(process.cwd(), 'index.html'),
'utf-8'
)
// 应用 Vite HTML 转换
template = await vite.transformIndexHtml(url, template)
// 加载服务器入口
const { render } = await vite.ssrLoadModule('/src/entry-server.js')
// 渲染应用 HTML
const { html: appHtml, preloadLinks } = await render(url)
// 将应用渲染的 HTML 注入模板
const html = template
.replace(`<!--preload-links-->`, preloadLinks)
.replace(`<!--app-html-->`, appHtml)
// 发送渲染的 HTML
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
} catch (e) {
vite.ssrFixStacktrace(e)
console.error(e)
res.status(500).end(e.message)
}
})
app.listen(3000)
}
createServer()
使用 Vite 进行测试
使用 Vitest 进行单元测试
Vitest 是一个 Vite 原生的单元测试框架:
# 安装 Vitest
npm install -D vitest
在 vite.config.js
中配置 Vitest:
import { defineConfig } from 'vite'
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
coverage: {
reporter: ['text', 'json', 'html']
}
}
})
编写测试:
// src/utils.test.js
import { describe, it, expect } from 'vitest'
import { sum } from './utils'
describe('sum 函数', () => {
it('正确相加两个数字', () => {
expect(sum(1, 2)).toBe(3)
})
it('处理负数', () => {
expect(sum(-1, -2)).toBe(-3)
})
})
运行测试:
npx vitest
组件测试
使用 Vitest 和 Vue Test Utils 测试 Vue 组件:
npm install -D @vue/test-utils jsdom
// src/components/Counter.test.js
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import Counter from './Counter.vue'
describe('Counter.vue', () => {
it('点击按钮时增加计数', async () => {
const wrapper = mount(Counter)
expect(wrapper.text()).toContain('计数: 0')
await wrapper.find('button').trigger('click')
expect(wrapper.text()).toContain('计数: 1')
})
})
端到端测试
使用 Cypress 与 Vite:
npm install -D cypress
为 Vite 配置 Cypress:
// cypress.config.js
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:5173',
supportFile: false
}
})
编写 E2E 测试:
// cypress/e2e/basic.cy.js
describe('基本测试', () => {
it('访问应用根 URL', () => {
cy.visit('/')
cy.contains('h1', '你好 Vite')
})
it('增加计数器', () => {
cy.visit('/')
cy.contains('计数: 0')
cy.get('button').contains('增加').click()
cy.contains('计数: 1')
})
})
部署
静态站点部署
为生产环境构建 Vite 项目:
npm run build
构建输出默认将在 dist
目录中,您可以将其部署到任何静态托管服务:
- Netlify
- Vercel
- GitHub Pages
- AWS S3 + CloudFront
- Firebase Hosting
- Cloudflare Pages
部署到 Netlify 的示例:
# 安装 Netlify CLI
npm install -g netlify-cli
# 部署到 Netlify
netlify deploy --prod --dir=dist
基础路径配置
如果您的站点部署到子目录,请配置基础路径:
// vite.config.js
export default defineConfig({
base: '/my-app/'
})
不同环境的环境变量
创建特定环境的文件:
.env # 在所有环境中加载
.env.local # 在所有环境中加载,被 git 忽略
.env.development # 在开发模式中加载
.env.production # 在生产模式中加载
性能优化
代码分割
Vite 自动对动态导入执行代码分割:
// 异步组件加载
const UserProfile = () => import('./components/UserProfile.vue')
// 带有注释的动态导入,用于块命名
import(/* webpackChunkName: "admin" */ './admin.js')
预加载
使用 <link rel="modulepreload">
预加载模块:
// vite.config.js
export default defineConfig({
build: {
modulePreload: {
polyfill: true
}
}
})
资源优化
在构建过程中优化资源:
// vite.config.js
export default defineConfig({
build: {
assetsInlineLimit: 4096, // 4kb
cssCodeSplit: true,
rollupOptions: {
output: {
assetFileNames: 'assets/[name].[hash].[ext]'
}
}
}
})
与框架的集成
Vue
# 使用 Vite 创建 Vue 项目
npm create vite@latest my-vue-app -- --template vue
在 vite.config.js
中配置 Vue:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
plugins: [
vue({
reactivityTransform: true
}),
vueJsx()
]
})
React
# 使用 Vite 创建 React 项目
npm create vite@latest my-react-app -- --template react
在 vite.config.js
中配置 React:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
react({
// 使用 React 插件选项
babel: {
plugins: ['babel-plugin-macros']
},
jsxImportSource: '@emotion/react'
})
]
})
Svelte
# 使用 Vite 创建 Svelte 项目
npm create vite@latest my-svelte-app -- --template svelte
在 vite.config.js
中配置 Svelte:
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [
svelte({
compilerOptions: {
dev: !process.env.PRODUCTION
}
})
]
})
最佳实践
项目结构
组织您的 Vite 项目以提高可维护性:
src/
├── assets/ # 静态资源
├── components/ # 可重用组件
├── composables/ # 组合式 API 函数(Vue)
├── hooks/ # 自定义钩子(React)
├── layouts/ # 布局组件
├── pages/ # 页面组件
├── router/ # 路由配置
├── stores/ # 状态管理
├── styles/ # 全局样式
├── utils/ # 实用函数
└── main.js # 入口点
性能提示
使用动态导入进行代码分割:
javascriptconst AdminPanel = () => import('./components/AdminPanel.vue')
使用 Intersection Observer 懒加载图片:
javascriptimport { useIntersectionObserver } from '@vueuse/core' const image = ref(null) const isVisible = ref(false) useIntersectionObserver(image, ([{ isIntersecting }]) => { if (isIntersecting) { isVisible.value = true } })
优化依赖:
bash# 分析包大小 npx vite-bundle-analyzer
使用支持树摇的导入:
javascript// ❌ 不好 - 导入整个库 import lodash from 'lodash' // ✅ 好 - 只导入需要的部分 import { debounce } from 'lodash-es'
安全最佳实践
净化用户输入以防止 XSS 攻击:
javascriptimport DOMPurify from 'dompurify' const sanitizedHTML = DOMPurify.sanitize(userInput)
使用环境变量存储敏感信息:
javascriptconst apiKey = import.meta.env.VITE_API_KEY
在生产环境中设置适当的 CSP 头:
javascript// 服务器中间件 app.use((req, res, next) => { res.setHeader( 'Content-Security-Policy', "default-src 'self'; script-src 'self'" ) next() })
故障排除
常见问题和解决方案
HMR 不工作:
- 检查文件是否正确导入
- 确保模块有适当的 HMR 接受代码
- 检查语法错误
构建错误:
- 检查循环依赖
- 验证所有导入是否正确
- 查找 package.json 中缺少的依赖
CSS 处理问题:
- 确保已安装预处理器包
- 检查预处理器语法
- 验证 vite.config.js 中的配置
环境变量不工作:
- 确保变量以
VITE_
为前缀 - 检查 .env 文件是否在项目根目录
- 更改 .env 文件后重启开发服务器
- 确保变量以
调试提示
启用源映射:
javascript// vite.config.js export default defineConfig({ build: { sourcemap: true } })
使用 Vite 的调试日志:
bash# 设置 DEBUG 环境变量 DEBUG=vite:* npm run dev
在浏览器的开发者工具中检查网络请求
通过一个一个禁用插件来检查插件冲突
常见问题解答
Vite 与 webpack 相比如何?
Vite 与 webpack 在几个关键方面有所不同:
- Vite 在开发过程中使用原生 ES 模块,而 webpack 打包所有内容
- Vite 具有更快的启动时间和 HMR
- Webpack 拥有更大的生态系统和更成熟的工具
- Vite 使用 Rollup 进行生产构建,对现代代码可能更高效
我可以在现有项目中使用 Vite 吗?
是的,您可以将现有项目迁移到 Vite:
- 安装 Vite 和必要的插件
- 创建
vite.config.js
文件 - 更新导入语句以使用 ES 模块语法
- 调整 HTML 入口点
- 更新 package.json 中的脚本
如何处理旧版浏览器?
使用 @vitejs/plugin-legacy
插件:
// vite.config.js
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
plugins: [
legacy({
targets: ['defaults', 'not IE 11']
})
]
})
如何为 monorepo 配置 Vite?
对于 monorepo,您可以使用:
- npm/yarn/pnpm 的工作区包
- Vite 的
optimizeDeps.include
选项包含工作区依赖 - 使用
defineConfig
和组合共享配置
// vite.config.js
export default defineConfig({
optimizeDeps: {
include: ['@my-org/shared-lib']
},
resolve: {
preserveSymlinks: true
}
})
相关资源
本文档将持续更新,如有问题请通过 GitHub Issues 反馈。