4、构建工具之Vite:Vite原理(ESM + esbuild),Vite项目初始化,Vite配置(别名/代理/环境变量),Vite插件机制,Vite与Webpack对比

好,咱们今天聊聊 Vite。说实话,Webpack 统治了前端构建好多年,但 Vite 的出现,确实让整个开发体验上了一个台阶。我记得我第一次用 Vite 启动项目时,那个秒开的开发服务器,真的让我愣了一下——嗯,这才对嘛。

4.1 Vite 的核心原理:ESM + esbuild

Vite 为什么这么快?说白了,它抓住了两个关键点:ES Modulesesbuild

传统工具(比如 Webpack)在开发时,得先把所有模块打包成一个 bundle,再启动服务器。项目一大,这个过程就慢得让人抓狂。Vite 的思路完全不同——它利用浏览器原生支持的 ES Module 加载能力。

你在代码里写 import,浏览器直接发 HTTP 请求去拿对应的文件。Vite 只需要在请求到达时,对单个文件做轻量转换,然后返回给浏览器。这就叫「按需编译」。

核心区别一句话:Webpack 是「先打包,再启动」;Vite 是「先启动,再按需编译」。

那 esbuild 又是什么角色?它负责两件事:

  • 预构建依赖:把 node_modules 里的 CommonJS 模块转成 ESM 格式,并合并成少量文件,减少请求数。
  • 开发时的转换:比如 TypeScript、JSX 的编译,esbuild 用 Go 写的,速度比 Babel 快几十倍。

我在项目中遇到过一个问题:某个第三方库是 CommonJS 格式,Vite 预构建时自动处理了,但如果你用了动态 require(),它可能就报错。这时候你需要在 optimizeDeps.exclude 里排除它,或者手动配置 optimizeDeps.include

小提示:Vite 的生产构建用的是 Rollup,不是 esbuild。因为 esbuild 的代码分割和 CSS 处理还不够成熟。开发和生产用两套工具,各取所长。

4.2 Vite 项目初始化

初始化一个 Vite 项目,简单到令人发指。你只需要一行命令:

npm create vite@latest my-vite-app -- --template vue-ts

或者用 yarn:

yarn create vite my-vite-app --template react-ts

它会问你选什么框架模板。我个人习惯用 vue-tsreact-ts,因为 TypeScript 现在几乎是标配了。

初始化完成后,目录结构大概是这样的:

my-vite-app/
├── index.html          # 入口 HTML,Vite 把它当入口文件
├── package.json
├── tsconfig.json
├── vite.config.ts      # 核心配置文件
├── src/
│   ├── main.ts
│   ├── App.vue
│   └── ...
└── public/

注意,index.html 直接放在根目录,而不是 public 里。Vite 把它当作模块图的入口,里面引用的 <script type="module" src="/src/main.ts"> 就是启动点。

避坑指南:我曾经把 index.html 放到 public 目录下,结果 Vite 死活不识别。记住,index.html 必须在项目根目录,public 只放静态资源(图片、字体等)。

4.3 Vite 配置:别名、代理、环境变量

Vite 的配置集中在 vite.config.ts 里。咱们一个一个说。

4.3.1 路径别名

你肯定不想写 ../../components/ 这种路径。配置别名很简单:

import { defineConfig } from 'vite'
import path from 'path'

export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
    }
  }
})

然后你在代码里就能写 import Button from '@/components/Button.vue'。记得在 tsconfig.json 里也加上对应的 paths 配置,不然 TypeScript 会报红。

4.3.2 代理配置

开发时跨域问题,Vite 用 server.proxy 解决:

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

这样你请求 /api/users 就会被代理到 http://localhost:3000/users。我在项目中遇到过一个问题:如果后端用了 WebSocket,记得加上 ws: true

4.3.3 环境变量

Vite 用 import.meta.env 来访问环境变量。默认有三个:

  • import.meta.env.MODE:当前模式(development/production)
  • import.meta.env.BASE_URL:部署基础路径
  • import.meta.env.PROD:是否为生产环境

自定义环境变量需要以 VITE_ 开头,放在 .env 文件中:

# .env
VITE_API_BASE_URL=https://api.example.com

# .env.development
VITE_API_BASE_URL=http://localhost:3000

然后在代码里用 import.meta.env.VITE_API_BASE_URL 读取。注意,只有 VITE_ 开头的变量才会暴露给客户端,其他变量只在服务端配置中可用。

小技巧:如果你需要类型提示,可以在 src/env.d.ts 里声明:interface ImportMetaEnv { readonly VITE_API_BASE_URL: string }

4.4 Vite 插件机制

Vite 的插件系统基于 Rollup 插件接口,但扩展了一些 Vite 特有的钩子。说白了,插件就是一个对象,包含一些生命周期函数。

一个最简单的插件长这样:

// my-plugin.ts
export default function myPlugin() {
  return {
    name: 'my-plugin',
    transform(code, id) {
      if (id.endsWith('.vue')) {
        // 对 .vue 文件做点什么
        return code.replace(/console\.log/g, '')
      }
    }
  }
}

然后在 vite.config.ts 里使用:

import myPlugin from './my-plugin'

export default defineConfig({
  plugins: [myPlugin()]
})

Vite 的插件钩子分为两类:

  • 通用钩子:和 Rollup 共享,比如 resolveIdloadtransformgenerateBundle
  • Vite 特有钩子:比如 config(修改配置)、configureServer(配置开发服务器)、handleHotUpdate(处理 HMR)

我在项目中写过一个小插件,用来在开发时自动生成路由配置。用 configureServer 钩子监听文件变化,然后动态更新路由表。嗯,这个思路你可以参考一下。

常用插件推荐:

  • @vitejs/plugin-vue:Vue 单文件组件支持
  • @vitejs/plugin-react:React JSX 支持
  • vite-plugin-pages:基于文件系统的路由
  • vite-plugin-svg-icons:SVG 图标管理

4.5 Vite 与 Webpack 对比

咱们来做个对比,这样你心里更有数。

维度 Vite Webpack
开发服务器启动 秒级(按需编译) 分钟级(全量打包)
热更新速度 毫秒级(只更新修改的模块) 秒级(需要重新打包)
构建工具 开发用 esbuild,生产用 Rollup 自研打包器
配置复杂度 低(开箱即用) 高(需要大量配置)
插件生态 较新,但增长快 成熟,插件丰富
浏览器兼容 生产构建支持 ES2015+ 可配置到 ES5
适用场景 新项目、现代浏览器 老项目、需要兼容 IE

你可能会问:那是不是所有新项目都应该用 Vite?我的答案是:大部分是,但不是全部

如果你要兼容 IE11,或者项目里用了很多 Webpack 特有的 loader 和插件,那迁移成本可能比较高。但如果你是从零开始,或者项目已经用上了 ES Module,Vite 绝对是更好的选择。

避坑指南:我曾经把一个用了大量 Webpack 自定义 loader 的老项目迁移到 Vite,结果发现有些 loader 没有 Vite 替代品,最后不得不自己写插件。所以,迁移前一定要评估好插件生态的覆盖度。

好了,Vite 的核心内容就这些。从原理到配置,从插件到对比,你应该能感受到 Vite 的设计哲学——用现代浏览器的能力,换取开发者的效率。下一章咱们聊聊构建产物的优化,包括代码分割、Tree Shaking 和缓存策略,这些在生产环境中非常关键。