别再忍受Webpack龟速构建了!Vite以10-100倍速度碾压,现代化前端开发新标准

别再忍受Webpack龟速构建了!Vite以10-100倍速度碾压,现代化前端开发新标准

别再忍受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

让你的前端开发体验焕然一新!

如果内容对您有帮助,欢迎打赏

您的支持是我继续创作的动力

前往打赏页面

评论区

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注