别再忍受Webpack龟速构建了!Vite以10-100倍速度碾压,现代化前端开发新标准
为什么 Vite 值得关注 / 为什么值得关注
在前端开发领域,构建工具的效率直接影响着开发体验和生产力。长期以来,Webpack 凭借其强大的生态系统和灵活性成为前端构建工具的标准选择,但随着项目规模不断增长,Webpack 的构建速度越来越成为开发者的痛点。一次完整的热更新可能需要等待数十秒,一个简单的代码修改可能触发整个项目的重新构建。这种痛苦相信每个大型前端项目的开发者都深有体会。
Vite 由 Vue.js 作者尤雨溪发起,由 Rollup 的作者 Luke Edwards 共同参与设计,是新一代前端构建工具的代表。与传统构建工具不同,Vite 利用浏览器原生 ES 模块(ESM)的能力,在开发阶段实现真正的按需编译。开发服务器启动时间从原来的数十秒甚至几分钟,缩短到几百毫秒甚至更快。热模块替换(HMR)的响应速度也达到了毫秒级别,真正实现了“修改即所见”的开发体验。
除了速度优势,Vite 还带来了极简的配置体验。零配置即可启动一个现代化的前端项目,同时支持 Vue、React、Preact、Svelte 等主流框架。Vite 内置了 TypeScript、JSX、CSS 预处理器等常见功能的支持,开箱即用。生产环境则使用 Rollup 进行打包,确保输出产物体积最优。
环境搭建 / Getting Started
前置要求
在开始使用 Vite 之前,需要确保本地环境满足以下要求:
Node.js 版本要求
Vite 需要 Node.js 版本 14.18+ 或 16+。推荐使用最新的 LTS 版本以获得最佳性能和稳定性。可以通过以下命令检查当前 Node.js 版本:
node --version
如果版本不符合要求,推荐使用 nvm(Node Version Manager)来管理多个 Node.js 版本:
# 安装 nvm(macOS/Linux)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# 安装完成后,重新加载配置文件
source ~/.bashrc # 或 source ~/.zshrc
# 安装最新的 LTS 版本
nvm install --lts
nvm use --lts
# 验证安装
node --version
npm --version
安装 Vite
Vite 可以通过两种方式使用:通过 npm 包全局安装,或者直接使用 npm create 命令创建项目。
方式一:使用 npm create 创建项目
这是最推荐的快速启动方式,Vite 提供了交互式的项目创建向导:
# 创建 Vue 项目
npm create vite@latest my-vue-app -- --template vue
# 创建 Vue + TypeScript 项目
npm create vite@latest my-vue-app -- --template vue-ts
# 创建 React 项目
npm create vite@latest my-react-app -- --template react
# 创建 React + TypeScript 项目
npm create vite@latest my-react-app -- --template react-ts
# 创建 Preact 项目
npm create vite@latest my-preact-app -- --template preact
# 创建 Vanilla JavaScript 项目
npm create vite@latest my-vanilla-app -- --template vanilla
# 创建 Vanilla TypeScript 项目
npm create vite@latest my-vanilla-app -- --template vanilla-ts
方式二:手动安装 Vite
对于现有项目,可以通过 npm 将 Vite 作为开发依赖安装:
# 进入项目目录
cd my-existing-project
# 初始化项目(如果还没有 package.json)
npm init -y
# 安装 Vite 和 Vue(如果是 Vue 项目)
npm install vite vue
# 或者同时安装 Vue 和 Vue 组件编译器
npm install vite vue @vitejs/plugin-vue
项目结构
使用 Vite 模板创建的项目具有清晰的标准结构。以 Vue 项目为例:
my-vue-app/
├── public/ # 静态资源目录,原样复制到输出目录
│ └── vite.svg # 示例静态资源
├── src/ # 源代码目录
│ ├── assets/ # 资源文件(图片、字体等)
│ │ └── vue.svg # 示例图片
│ ├── components/ # Vue 组件目录
│ │ └── HelloWorld.vue # 示例组件
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── index.html # HTML 入口文件(重要!)
├── package.json # 项目配置文件
├── vite.config.js # Vite 配置文件
└── README.md # 项目说明文档
需要特别注意的是,Vite 的入口文件是 index.html 而不是 JavaScript 文件。这是 Vite 的设计特点之一,使得项目结构更加直观。
启动开发服务器
项目创建完成后,进入项目目录并启动开发服务器:
# 进入项目目录
cd my-vue-app
# 安装项目依赖
npm install
# 启动开发服务器
npm run dev
开发服务器启动后,会在终端显示类似以下信息:
VITE v5.0.0 ready in 320 ms
➜ Local: http://localhost:5173/
➜ Network: http://192.168.1.100:5173/
➜ press h + enter to show help
现在,打开浏览器访问 http://localhost:5173/,即可看到基于 Vite 的应用运行效果。
构建生产版本
当项目开发完成后,需要构建用于生产环境的版本:
# 构建生产版本
npm run build
# 构建完成后预览生产版本
npm run preview
构建产物会输出到 dist 目录(可通过 vite.config.js 修改输出目录)。npm run preview 命令会启动一个本地服务器来预览生产构建结果。
核心功能详解 / Core Features
1. 基于 ESM 的极速开发体验
Vite 的核心创新在于开发阶段使用浏览器原生的 ES 模块系统。传统构建工具(如 Webpack)需要在启动开发服务器前,先将所有模块打包成一个或多个文件,这个过程在大型项目中可能需要数十秒甚至几分钟。
Vite 的工作原理完全不同。当浏览器请求某个模块时,Vite 的开发服务器会按需编译该模块,而不是一次性打包整个项目。这种“服务即打包”的模式带来了几个显著优势:
首先,启动时间与项目规模无关。无论项目有多大,Vite 的开发服务器启动时间都保持在毫秒级别。其次,热更新速度快。由于只重新编译变化的模块,HMR 的响应时间通常在 50 毫秒以内。第三,开发体验更接近原生开发。开发者可以立即看到修改结果,而不需要等待漫长的构建过程。
2. 预构建依赖优化
虽然 Vite 使用原生 ESM,但 npm 包中的大多数模块仍然使用 CommonJS 格式。为了确保浏览器环境下的兼容性,Vite 会使用 esbuild 对这些依赖进行预构建。esbuild 是用 Go 语言编写的极速 JavaScript 打包工具,其打包速度比传统 JavaScript 工具快 10-100 倍。
预构建过程会自动处理以下问题:转换 CommonJS 模块为 ESM 格式、合并多个相关的内部模块以减少网络请求、将 TypeScript 的类型声明文件排除在外以加快加载速度。
// vite.config.js
export default defineConfig({
optimizeDeps: {
// 预构建包含的依赖(通常不需要修改)
include: ['vue', 'vue-router', 'pinia'],
// 排除不需要预构建的依赖
exclude: ['some-esm-only-package']
}
})
3. 智能 CSS 处理
Vite 对 CSS 的处理既强大又简单。Vite 原生支持 CSS Modules、CSS 预处理器、PostCSS 等主流 CSS 解决方案,无需额外配置。
CSS Modules 可以自动启用,只需将 CSS 文件命名为 xxx.module.css 即可:
/* Button.module.css */
.button {
padding: 8px 16px;
border-radius: 4px;
}
.primary {
background-color: #3b82f6;
color: white;
}
// 组件中使用
import styles from './Button.module.css'
export default {
render() {
return `<button class="${styles.button} ${styles.primary}">
点击我
</button>`
}
}
CSS 预处理器支持 SCSS、Sass、Less、Stylus。安装对应的预处理器后即可使用:
# 安装 SCSS
npm install -D sass
# 安装 Less
npm install -D less
# 安装 Stylus
npm install -D stylus
/* styles/variables.scss */
$primary-color: #3b82f6;
$border-radius: 8px;
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
CSS 注入模式可以控制样式如何被注入到页面中:
// vite.config.js
export default defineConfig({
css: {
// 'injected' - 样式内联到 JavaScript 中(默认)
// 'external' - 样式输出为单独的 CSS 文件
// 'preprocessor-only' - 仅预处理,不注入
devSourcemap: true, // 开发环境下启用 sourcemap
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
}
})
4. TypeScript 原生支持
Vite 内置了 TypeScript 的编译支持,开箱即用。Vite 使用 esbuild 进行 TypeScript 的转译,速度比传统 tsc 快 20-30 倍。需要注意 esbuild 的转译只做语法转换,不会进行类型检查。
// vite.config.ts
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')
}
},
// TypeScript 配置
esbuild: {
// 目标浏览器
target: 'es2015',
// 是否移除注释
keepComments: false
},
// 替代 tsconfig.json 中的某些配置
vueCompilerOptions: {
// Vue 3.2+ 的 defineProps 优化
experimentalDefinePropProposal: 'kaleidoscope'
}
})
Vite 的 tsconfig.json 配置通常如下:
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
5. 插件系统
Vite 的插件系统与 Rollup 兼容,同时提供了一些 Vite 特有的钩子。这使得 Rollup 的插件可以无缝在 Vite 中使用,同时也允许开发者编写同时适用于开发和生产环境的插件。
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
vue(),
// Mock 数据插件
viteMockServe({
mockPath: './mock',
enable: true
}),
// 自动导入 API
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
dts: 'src/auto-imports.d.ts'
})
]
})
常用插件推荐:
# Vue 生态
npm install -D @vitejs/plugin-vue # Vue 3 支持
npm install -D @vitejs/plugin-vue-jsx # Vue JSX 支持
# React 生态
npm install -D @vitejs/plugin-react # React 支持
# 构建优化
npm install -D vite-plugin-compression # gzip 压缩
npm install -D vite-plugin-html # HTML 处理
npm install -D vite-plugin-svg-icons # SVG 图标
# 开发工具
npm install -D vite-plugin-mock # Mock 数据
npm install -D unplugin-auto-import # 自动导入 API
npm install -D unplugin-vue-components # 自动导入组件
6. 环境变量与模式
Vite 使用环境变量文件(.env 文件)来管理不同环境下的配置。Vite 会根据模式(mode)加载对应的环境变量文件。
.env # 所有模式下都会加载
.env.local # 所有模式下都会加载,会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,会被 git 忽略
# .env.development - 开发环境
VITE_APP_TITLE=我的应用(开发)
VITE_API_BASE_URL=http://localhost:3000/api
VITE_ENABLE_MOCK=true
# .env.production - 生产环境
VITE_APP_TITLE=我的应用
VITE_API_BASE_URL=https://api.example.com
VITE_ENABLE_MOCK=false
在代码中使用环境变量:
// 访问环境变量 - 必须以 VITE_ 开头
console.log(import.meta.env.VITE_API_BASE_URL)
// 访问所有内置环境变量
console.log(import.meta.env.MODE) // 'development' | 'production' | 'test'
console.log(import.meta.env.BASE_URL) // 部署基础路径
console.log(import.meta.env.PROD) // 是否为生产环境
console.log(import.meta.env.DEV) // 是否为开发环境
console.log(import.meta.env.SSR) // 是否为 SSR 模式
// TypeScript 类型提示
// 在 src/env.d.ts 中定义
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string
readonly VITE_APP_TITLE: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
实战教程 / Step-by-Step Tutorial
实战一:从零创建 Vue 3 + TypeScript 项目
让我们从头开始,创建一个完整的 Vue 3 + TypeScript 项目。
第一步:创建项目基础结构
# 创建项目
npm create vite@latest my-project -- --template vue-ts
cd my-project
npm install
第二步:安装项目所需的依赖
# Vue 生态核心库
npm install vue vue-router pinia
# UI 组件库(可选)
npm install element-plus
# HTTP 请求库
npm install axios
# 开发依赖
npm install -D @vitejs/plugin-vue sass
第三步:配置路由
// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import NotFound from '@/views/NotFound.vue'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
component: Home,
meta: { title: '首页' }
},
{
path: '/about',
name: 'About',
component: About,
meta: { title: '关于我们' }
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound,
meta: { title: '页面未找到' }
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
// 路由守卫
router.beforeEach((to, from, next) => {
// 设置页面标题
document.title = `${to.meta.title || ''} - 我的应用`
next()
})
export default router
第四步:配置状态管理
// src/store/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { UserInfo } from '@/types'
export const useUserStore = defineStore('user', () => {
// 状态
const userInfo = ref<UserInfo | null>(null)
const token = ref<string>('')
// 计算属性
const isLoggedIn = computed(() => !!token.value)
const userName = computed(() => userInfo.value?.name || '访客')
// 方法
async function login(username: string, password: string) {
// 模拟登录请求
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
})
if (response.ok) {
const data = await response.json()
token.value = data.token
userInfo.value = data.userInfo
return true
}
return false
}
function logout() {
token.value = ''
userInfo.value = null
}
return {
userInfo,
token,
isLoggedIn,
userName,
login,
logout
}
})
第五步:创建类型定义
// src/types/index.ts
export interface UserInfo {
id: number
name: string
email: string
avatar?: string
roles: string[]
}
export interface Article {
id: number
title: string
content: string
author: UserInfo
createdAt: string
tags: string[]
}
第六步:编写主应用组件
<!-- src/App.vue -->
<script setup lang="ts">
import { RouterView } from 'vue-router'
import AppHeader from '@/components/AppHeader.vue'
import AppFooter from '@/components/AppFooter.vue'
</script>
<template>
<div class="app-container">
<AppHeader />
<main class="main-content">
<RouterView v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</RouterView>
</main>
<AppFooter />
</div>
</template>
<style>
/* 全局样式 */
:root {
--primary-color: #3b82f6;
--text-color: #333;
--bg-color: #f5f5f5;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, sans-serif;
color: var(--text-color);
background-color: var(--bg-color);
}
.app-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.main-content {
flex: 1;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
/* 路由过渡动画 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
第七步:创建视图组件
<!-- src/views/Home.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { Article } from '@/types'
import ArticleCard from '@/components/ArticleCard.vue'
const articles = ref<Article[]>([])
const loading = ref(false)
async function fetchArticles() {
loading.value = true
try {
// 模拟 API 请求
await new Promise(resolve => setTimeout(resolve, 500))
articles.value = [
{
id: 1,
title: 'Vite 实战入门',
content: 'Vite 是一款现代化的前端构建工具...',
author: { id: 1, name: '张三', email: 'zhangsan@example.com', roles: ['admin'] },
createdAt: '2024-01-15',
tags: ['Vite', '前端']
},
{
id: 2,
title: 'Vue 3 组合式 API 指南',
content: '组合式 API 是 Vue 3 的核心特性...',
author: { id: 2, name: '李四', email: 'lisi@example.com', roles: ['user'] },
createdAt: '2024-01-14',
tags: ['Vue', 'JavaScript']
}
]
} finally {
loading.value = false
}
}
onMounted(() => {
fetchArticles()
})
</script>
<template>
<div class="home">
<section class="hero">
<h1>欢迎来到我的博客</h1>
<p>分享技术与生活的点点滴滴</p>
</section>
<section class="articles">
<div v-if="loading" class="loading">加载中...</div>
<template v-else>
<ArticleCard
v-for="article in articles"
:key="article.id"
:article="article"
/>
</template>
</section>
</div>
</template>
<style scoped lang="scss">
.hero {
text-align: center;
padding: 60px 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
margin-bottom: 40px;
color: white;
h1 {
font-size: 2.5rem;
margin-bottom: 12px;
}
p {
font-size: 1.2rem;
opacity: 0.9;
}
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.articles {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 24px;
}
</style>
实战二:配置 Vite 高级选项
让我们深入了解 Vite 的配置,实现更高级的功能。
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
// 项目根目录
root: '.',
// 公共基础路径
// 用于部署到不同路径时调整
base: '/',
// 公共目录,静态资源会原样复制
publicDir: 'public',
// 构建输出目录
build: {
// 输出目录
outDir: 'dist',
// 生成资源的目录
assetsDir: 'assets',
// 开启 gzip 压缩
compressGzip: true,
// 关闭 gzip 压缩
compressBrotli: false,
// 小于此阈值的资源将内联为 base64
assetsInlineLimit: 4096,
// 手动拆分 chunk
rollupOptions: {
output: {
// 手动指定入口文件
entryFileNames: 'js/[name]-[hash].js',
// 手动指定 chunk 文件名
chunkFileNames: 'js/[name]-[hash].js',
// 手动指定静态资源文件名
assetFileNames: '[ext]/[name]-[hash].[ext]',
// 分块策略
manualChunks: {
'vendor': ['vue', 'vue-router', 'pinia'],
'element-plus': ['element-plus']
}
}
},
// 关闭源文件映射(生产环境)
sourcemap: false,
// 构建目标
target: 'es2015',
// 最小化混淆
minify: 'esbuild',
// CSS 代码分割
cssCodeSplit: true,
// 开启 CSS 作用域
cssTarget: 'chrome80',
// 清理 console 和 debugger
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
},
// 开发服务器配置
server: {
// 服务器端口
port: 5173,
// 服务器主机
host: '0.0.0.0',
// 是否自动打开浏览器
open: true,
// 是否开启 HTTPS
https: false,
// 代理配置
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
// 路径别名
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@views': path.resolve(__dirname, './src/views'),
'@utils': path.resolve(__dirname, './src/utils'),
'@assets': path.resolve(__dirname, './src/assets')
},
// 省略的扩展名
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
},
// 预构建依赖
optimizeDeps: {
include: ['vue', 'vue-router', 'pinia'],
exclude: []
},
// CSS 配置
css: {
devSourcemap: true,
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
},
// JSON 导入
json: {
namedExports: true
},
// esbuild 配置
esbuild: {
jsxFactory: 'h',
jsxFragment: 'Fragment',
jsxInject: `import React from 'react'`
},
// 依赖预构建
prebuildOptions: {},
// 日志级别
logLevel: 'info',
// 清除屏幕
clearScreen: true
})
实战三:使用 Mock 数据进行开发
前后端分离开发中,Mock 数据是提高开发效率的重要手段。
安装 Mock 插件
npm install -D vite-plugin-mock
创建 Mock 数据文件
// mock/article.ts
import { defineMock } from 'vite-plugin-mock'
export default defineMock([
{
// 接口路径
url: '/api/articles',
// 请求方法
method: 'get',
// 响应延迟(毫秒)
delay: 300,
// 响应数据
response: () => {
return {
code: 0,
message: 'success',
data: [
{
id: 1,
title: 'Vite 3.0 正式发布',
content: 'Vite 3.0 带来了众多新特性...',
author: '张三',
createdAt: '2024-01-15'
},
{
id: 2,
title: 'Vue 3.3 新特性一览',
content: 'Vue 3.3 引入了许多期待已久的特性...',
author: '李四',
createdAt: '2024-01-14'
}
]
}
}
},
{
url: '/api/article/:id',
method: 'get',
response: ({ params }) => {
return {
code: 0,
message: 'success',
data: {
id: params.id,
title: '文章标题',
content: '文章内容...',
author: '作者',
createdAt: new Date().toISOString()
}
}
}
},
{
url: '/api/login',
method: 'post',
response: ({ body }) => {
const { username, password } = JSON.parse(body as string)
if (username === 'admin' && password === '123456') {
return {
code: 0,
message: '登录成功',
data: {
token: 'mock-token-12345',
userInfo: {
id: 1,
name: '管理员',
email: 'admin@example.com'
}
}
}
}
return {
code: 401,
message: '用户名或密码错误',
data: null
}
}
}
])
配置 Vite 使用 Mock
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'
export default defineConfig({
plugins: [
vue(),
viteMockServe({
// Mock 文件路径
mockPath: './mock',
// 是否启用
enable: true,
// 是否显示请求日志
logger: true
})
]
})
在组件中使用 Mock 接口
// src/api/article.ts
import axios from 'axios'
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 10000
})
export interface Article {
id: number
title: string
content: string
author: string
createdAt: string
}
export interface ApiResponse<T> {
code: number
message: string
data: T
}
export async function getArticles(): Promise<Article[]> {
const response = await apiClient.get<ApiResponse<Article[]>>('/articles')
return response.data.data
}
export async function getArticleById(id: number): Promise<Article> {
const response = await apiClient.get<ApiResponse<Article>>(`/article/${id}`)
return response.data.data
}
export async function login(username: string, password: string) {
const response = await apiClient.post('/login', { username, password })
return response.data
}
实战四:实现 SSR(服务端渲染)
Vite 也支持服务端渲染场景,下面以 Vue 3 为例。
安装 SSR 依赖
npm install -D vite-ssr
创建 SSR 入口文件
// src/entry-server.js
import { renderToString } from 'vue/server-renderer'
export async function render(url, manifest) {
// 创建应用实例
const app = createApp()
// 创建 router
const router = createRouter()
router.push(url)
// 等待路由解析完成
await router.isReady()
// 渲染到字符串
const rendered = await renderToString(app)
return { rendered }
}
配置 Vite SSR
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue()
],
ssr: {
// SSR 入口文件
noExternal: ['vue', 'vue-router'],
// 不进行 SSR 的包
external: ['express', 'compression']
},
build: {
ssr: true,
ssrManifest: true
}
})
创建 Express 服务器
// server.js
import express from 'express'
import { createServer } from 'vite'
import { render } from './entry-server.js'
async function startServer() {
const app = express()
// 创建 Vite 开发服务器
const vite = await createServer({
server: { middlewareMode: true },
appType: 'custom'
})
// 使用 Vite 中间件
app.use(vite.middlewares)
// 处理 SSR 路由
app.use('*', async (req, res) => {
const url = req.originalUrl
try {
const { rendered } = await render(url)
const html = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SSR App</title>
</head>
<body>
<div id="app">${rendered}</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
`
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
} catch (e) {
vite.ssrFixStacktrace(e)
console.error(e.stack)
res.status(500).end(e.stack)
}
})
app.listen(3000, () => {
console.log('Server running at http://localhost:3000')
})
}
startServer()
常见使用场景 / Common Use Cases
场景一:多页面应用(MPA)
对于需要多个独立页面的项目,Vite 提供了简单高效的多页面支持:
// vite.config.js
import { resolve } from 'path'
import { defineConfig } from 'vite'
export default defineConfig({
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
about: resolve(__dirname, 'about.html'),
contact: resolve(__dirname, 'contact.html'),
// 添加更多页面
admin: resolve(__dirname, 'admin.html')
}
}
}
})
对应的 HTML 文件结构
<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>首页</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/pages/home/main.js"></script>
</body>
</html>
<!-- about.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>关于我们</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/pages/about/main.js"></script>
</body>
</html>
场景二:组件库开发
使用 Vite 开发组件库,既可以进行本地开发调试,又可以构建发布到 npm:
// packages/my-components/vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [
vue({
// Vue 3.2+ 的 JSX 支持
script: {
defineModel: true
}
})
],
build: {
lib: {
entry: resolve(__dirname, 'index.ts'),
name: 'MyComponents',
formats: ['es', 'umd', 'cjs'],
fileName: (format) => `my-components.${format}.js`
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue'
},
// 打包为单文件
inlineDynamicImports: true
}
},
cssCodeSplit: false
}
})
组件库入口文件
// packages/my-components/index.ts
// 导出所有组件
export { default as Button } from './src/Button.vue'
export { default as Input } from './src/Input.vue'
export { default as Dialog } from './src/Dialog.vue'
export { default as Select } from './src/Select.vue'
// 导出类型
export type { ButtonProps } from './src/types'
// 导出插件(用于 Vue.use)
import Button from './src/Button.vue'
import Input from './src/Input.vue'
const components = {
Button,
Input
}
const install = (app) => {
Object.entries(components).forEach(([name, component]) => {
app.component(name, component)
})
}
export default { install }
场景三:Electron 应用开发
Vite 可以与 Electron 结合使用,实现高效的桌面应用开发:
// electron/vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
base: './',
build: {
outDir: 'dist-electron',
rollupOptions: {
input: {
main: resolve(__dirname, 'electron/main.js'),
preload: resolve(__dirname, 'electron/preload.js')
}
}
},
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
}
})
场景四:静态站点生成(SSG)
Vite 可以配合 VitePress 或 Astro 等工具实现静态站点生成:
// vite.config.js - 使用 VitePress
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue()
],
// VitePress 特定配置
vitepress: {
siteConfig: {
title: '我的文档',
description: '基于 VitePress 的文档站点'
}
}
})
技巧与最佳实践 / Tips and Best Practices
性能优化技巧
1. 使用 Vite 的构建分析功能
npm install -D rollup-plugin-visualizer
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
vue(),
visualizer({
filename: 'stats.html',
open: true,
gzipSize: true,
brotliSize: true
})
]
})
构建完成后,打开生成的 stats.html 文件可以可视化分析打包结果。
2. 懒加载路由和组件
<!-- 不推荐:同步加载 -->
<script setup>
import HeavyComponent from './HeavyComponent.vue'
</script>
<!-- 推荐:异步加载 -->
<script setup>
import { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
</script>
// 路由懒加载
const routes = [
{
path: '/about',
component: () => import('./views/About.vue')
},
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue')
}
]
3. 图片和资源优化
// vite.config.js
import { defineConfig } from 'vite'
import viteImage from 'vite-plugin-image'
export default defineConfig({
plugins: [
viteImage({
// 图片格式支持
accept: ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp'],
// 是否压缩图片
compress: true,
// 压缩质量
quality: 80
})
]
})
开发体验优化
1. 使用路径别名提高开发效率
// 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'),
'@components': path.resolve(__dirname, './src/components'),
'@views': path.resolve(__dirname, './src/views'),
'@utils': path.resolve(__dirname, './src/utils'),
'@api': path.resolve(__dirname, './src/api'),
'@store': path.resolve(__dirname, './src/store'),
'@types': path.resolve(__dirname, './src/types'),
'@styles': path.resolve(__dirname, './src/styles')
}
}
})
同时更新 tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@views/*": ["./src/views/*"],
"@utils/*": ["./src/utils/*"],
"@api/*": ["./src/api/*"],
"@store/*": ["./src/store/*"],
"@types/*": ["./src/types/*"],
"@styles/*": ["./src/styles/*"]
}
}
}
2. 配置 ESLint 和 Prettier
// .eslintrc.cjs
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended'
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: ['vue', '@typescript-eslint'],
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'warn'
}
}
// .prettierrc.js
module.exports = {
semi: false,
singleQuote: true,
trailingComma: 'none',
printWidth: 100,
arrowParens: 'avoid',
endOfLine: 'auto',
vueIndentScriptAndStyle: false
}
3. 使用 husky 和 lint-staged 实现提交检查
npm install -D husky lint-staged
// package.json
{
"scripts": {
"prepare": "husky install"
},
"lint-staged": {
"*.{js,ts,vue}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss}": [
"prettier --write"
]
}
}
# 初始化 husky
npx husky install
# 添加 pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"
生产环境最佳实践
1. 启用资源预加载
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
// 手动分包
manualChunks: (id) => {
if (id.includes('node_modules')) {
if (id.includes('vue')) {
return 'vue-vendor'
}
if (id.includes('element-plus')) {
return 'element-vendor'
}
return 'vendor'
}
},
// 资源清单文件
manifest: true
}
}
}
})
2. 配置正确的缓存策略
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
// 使用内容哈希命名,利于缓存
entryFileNames: 'js/[name]-[hash].js',
chunkFileNames: 'js/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]'
}
}
}
})
在部署时配置服务器的缓存策略:
# Nginx 配置示例
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.(png|jpg|svg|ico)$ {
expires 6m;
add_header Cache-Control "public";
}
3. 启用 Brotli 压缩
// vite.config.js
import compressPlugin from 'vite-plugin-compression'
export default defineConfig({
plugins: [
compressPlugin({
algorithm: 'brotliCompress',
ext: '.br',
threshold: 10240
})
]
})
调试技巧
1. 调试 JavaScript
在 Chrome DevTools 中使用断点调试,或使用 VS Code 的 debugger:
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Vite Debug",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/node_modules/vite/bin/vite.js",
"args": ["dev"],
"env": {
"NODE_ENV": "development"
}
}
]
}
2. 调试 CSS
启用 CSS sourcemap 可以帮助调试编译后的 CSS:
// vite.config.js
export default defineConfig({
css: {
devSourcemap: true
}
})
3. 查看构建日志详情
# 详细日志
DEBUG=* npm run build
# 或者在 vite.config.js 中配置
export default defineConfig({
logLevel: 'debug'
})
总结 / Conclusion
Vite 代表了前端构建工具的新一代设计理念,它通过充分利用现代浏览器的原生能力,实现了开发体验质的飞跃。从首次启动的毫秒级响应,到热更新的即时反馈,Vite 让开发者能够更加专注于业务逻辑的实现,而不是等待构建完成。
Vite 的核心优势可以总结为以下几点:
极速的开发体验:利用浏览器原生 ESM 和按需编译,Vite 将开发服务器的启动时间从分钟级缩短到毫秒级,热更新也从秒级缩短到毫秒级。这种即时的反馈循环极大地提升了开发效率和心情。
简洁的配置:零配置即可启动项目,同时提供丰富的配置选项满足高级需求。Vite 的配置设计遵循“约定优于配置”的原则,让新手能够快速上手,同时为专家提供足够的扩展性。
强大的插件生态:Vite 与 Rollup 兼容的插件系统意味着可以复用大量的前端生态工具。从 Vue 到 React,从静态网站到 SSR,Vite 都能提供良好的支持。
优化的生产构建:开发阶段追求速度,生产阶段追求质量。Vite 使用 Rollup 进行生产打包,输出高度优化的静态资源,配合各种优化策略确保应用性能最优。
随着前端技术的不断发展,Vite 正在成为越来越多项目的首选构建工具。它的设计理念和技术方案不仅影响了 Vite 本身,还推动了整个前端生态的进步。
相关资源链接
官方资源
Vite 官方文档(中文):https://cn.vitejs.dev/
Vite GitHub 仓库:https://github.com/vitejs/vite
Vite 官方插件列表:https://github.com/vitejs/awesome-vite
社区生态
awesome-vite:https://github.com/vitejs/awesome-vite
Vite Examples:https://github.com/vitejs/vite/tree/main/packages/playground
学习资源
Vite 官方博客:https://vitejs.dev/blog
Vue 3 官方文档:https://vuejs.org/
Rollup 插件文档:https://rollupjs.org/
相关工具
Vitest:https://vitest.dev/ – Vite 原生单元测试框架
VitePress:https://vitepress.vuejs.org/ – Vite 驱动的静态站点生成器
SvelteKit:https://kit.svelte.dev/ – 基于 Svelte 和 Vite 的全栈框架
Astro:https://astro.build/ – 内容驱动的静态站点构建工具
Nuxt:https://nuxt.com/ – Vue 3 全栈框架
Remix:https://remix.run/ – React 全栈框架
Vite 的出现标志着前端开发进入了一个新的阶段。它不仅解决了传统构建工具的速度痛点,还通过创新的技术方案重新定义了开发体验。无论你是正在选择新项目技术栈的决策者,还是寻找更好开发工具的工程师,又或者是对现代前端技术充满好奇的学习者,Vite 都值得你深入了解和尝试。
开始使用 Vite,只需一行命令:
npm create vite@latest
让你的前端开发体验焕然一新!
评论区