跳到主要内容

核心功能

模块热替换(HMR)

模块热替换(Hot Module Replacement,HMR)是 Vite 的核心特性之一。它允许在运行时更新模块而无需完全刷新页面,从而保留应用状态。

HMR 工作原理

Vite 通过原生 ESM 实现了极速的 HMR:

  1. 开发服务器建立 WebSocket 连接
  2. 文件修改触发服务器推送更新事件
  3. 浏览器接收事件并请求更新后的模块
  4. 新模块替换旧模块,应用状态保持不变

框架 HMR 支持

Vite 为流行框架提供官方 HMR 集成:

框架官方插件特点
Vue@vitejs/plugin-vue单文件组件完整支持
React@vitejs/plugin-reactFast Refresh
React SWC@vitejs/plugin-react-swc基于 SWC,更快
Preact@prefresh/vite轻量级
Sveltevite-plugin-svelte官方支持

手动处理 HMR

对于自定义模块,可以使用 Vite 提供的 HMR API:

// 检查当前模块是否支持 HMR
if (import.meta.hot) {
// 接受自身更新
import.meta.hot.accept()

// 接受特定依赖的更新
import.meta.hot.accept('./dependency.js', (newModule) => {
// 使用更新后的模块
console.log('dependency updated:', newModule)
})

// 监听数据变化
import.meta.hot.on('vite:beforeUpdate', (data) => {
console.log('before update:', data)
})

// 清理副作用
import.meta.hot.dispose(() => {
// 清理定时器、事件监听等
clearInterval(timer)
})
}

TypeScript 支持

Vite 原生支持 TypeScript,无需额外配置。

仅转译,不类型检查

Vite 使用 esbuild 转译 TypeScript,速度比 tsc 快 20-30 倍。但 Vite 不进行类型检查,原因如下:

  • 转译可以按文件进行,与 Vite 的按需编译模型完美契合
  • 类型检查需要了解整个模块图,会显著降低速度

推荐的类型检查方案

方案一:IDE 实时检查

现代 IDE(VS Code、WebStorm)会在编辑时实时显示类型错误。

方案二:构建时检查

{
"scripts": {
"build": "tsc --noEmit && vite build",
"type-check": "tsc --noEmit"
}
}

方案三:开发时检查(使用插件)

npm install -D vite-plugin-checker
// vite.config.js
import { defineConfig } from 'vite'
import checker from 'vite-plugin-checker'

export default defineConfig({
plugins: [
checker({ typescript: true }),
],
})

重要编译器选项

{
"compilerOptions": {
// 必须设置为 true
// esbuild 只执行转译,不支持某些需要类型信息的功能
"isolatedModules": true,

// ES2022 或更高版本默认为 true
// 与 TypeScript 4.3.2+ 行为一致
"useDefineForClassFields": true,

// 跳过库类型检查(避免依赖类型问题)
"skipLibCheck": true
}
}

客户端类型

为获得客户端代码的类型支持,添加 vite/client 类型:

{
"compilerOptions": {
"types": ["vite/client"]
}
}

或使用三斜线指令:

/// <reference types="vite/client" />

vite/client 提供以下类型:

  • 资源导入(如 .svg 文件)
  • import.meta.env 的类型
  • import.meta.hot HMR API 的类型

CSS 处理

Vite 对 CSS 提供开箱即用的支持。

基础 CSS

导入 .css 文件会自动将内容注入页面:

import './style.css'

CSS 预处理器

Vite 内置支持 Sass、Less、Stylus:

# Sass
npm install -D sass-embedded # 或 sass

# Less
npm install -D less

# Stylus
npm install -D stylus

使用:

<style lang="scss">
$primary: #42b983;

.container {
background: $primary;

// 嵌套规则
.title {
font-size: 20px;
}
}
</style>

CSS Modules

.module.css 结尾的文件被视为 CSS Modules:

/* example.module.css */
.red {
color: red;
}

.bold {
font-weight: bold;
}
import classes from './example.module.css'

document.getElementById('app').className = classes.red

启用 camelCase 命名:

// vite.config.js
export default defineConfig({
css: {
modules: {
localsConvention: 'camelCaseOnly',
},
},
})
// .apply-color -> applyColor
import { applyColor } from './example.module.css'

CSS 内联

使用 ?inline 查询参数阻止 CSS 自动注入:

import './foo.css'           // 自动注入到页面
import styles from './bar.css?inline' // 不注入,返回 CSS 字符串

PostCSS

如果项目包含有效的 PostCSS 配置,Vite 会自动应用:

// postcss.config.js
export default {
plugins: {
'postcss-preset-env': {},
autoprefixer: {},
},
}

静态资源处理

资源导入

导入静态资源会返回解析后的 URL:

import imgUrl from './img.png'

document.getElementById('hero-img').src = imgUrl

查询参数修饰符

查询参数说明示例
?url显式作为 URL 导入import url from './asset.js?url'
?raw作为字符串导入import content from './file.txt?raw'
?inline内联为 base64import svg from './icon.svg?inline'

资源内联

小于 build.assetsInlineLimit(默认 4KB)的资源会被内联为 base64:

// vite.config.js
export default defineConfig({
build: {
assetsInlineLimit: 4096, // 4KB
},
})

public 目录

public 目录下的文件会原样复制到输出目录,不会被处理:

public/
├── favicon.ico
├── robots.txt
└── images/
└── logo.png

引用 public 目录资源:

<!-- 使用根路径引用 -->
<img src="/images/logo.png" />

JSON 导入

JSON 文件可以直接导入,支持具名导入:

// 导入整个对象
import json from './example.json'

// 具名导入(有助于 tree-shaking)
import { field } from './example.json'

Glob 导入

Vite 支持通过 import.meta.glob 从文件系统导入多个模块:

基础用法

// 懒加载模式
const modules = import.meta.glob('./dir/*.js')

// 结果:
// {
// './dir/foo.js': () => import('./dir/foo.js'),
// './dir/bar.js': () => import('./dir/bar.js'),
// }

立即加载

const modules = import.meta.glob('./dir/*.js', { eager: true })

// 结果:
// {
// './dir/foo.js': { /* 模块内容 */ },
// './dir/bar.js': { /* 模块内容 */ },
// }

具名导入

const modules = import.meta.glob('./dir/*.js', {
import: 'setup',
eager: true,
})

// 结果:
// {
// './dir/foo.js': /* foo.js 的 setup 导出 */,
// './dir/bar.js': /* bar.js 的 setup 导出 */,
// }

多个模式

const modules = import.meta.glob([
'./dir/*.js',
'./another/*.js',
])

排除文件

const modules = import.meta.glob([
'./dir/*.js',
'!**/bar.js', // 排除 bar.js
])

动态导入

Vite 支持动态导入变量:

const module = await import(`./dir/${file}.js`)

动态导入规则

为安全起见,动态导入必须满足以下规则:

  1. 必须以 ./../ 开头
  2. 必须包含文件扩展名
  3. 同一目录的导入必须指定文件名模式
// ✅ 有效
import(`./dir/${foo}.js`)
import(`./prefix-${foo}.js`)

// ❌ 无效
import(`${foo}.js`) // 缺少 ./ 或 ../
import(`./dir/${foo}`) // 缺少扩展名
import(`./${foo}.js`) // 同一目录缺少前缀

Web Workers

构造函数方式(推荐)

const worker = new Worker(new URL('./worker.js', import.meta.url))

// 模块 Worker
const moduleWorker = new Worker(
new URL('./worker.js', import.meta.url),
{ type: 'module' }
)

查询后缀方式

import MyWorker from './worker?worker'

const worker = new MyWorker()

Worker 选项

// 内联为 base64
import InlineWorker from './worker?worker&inline'

// 获取 URL
import WorkerUrl from './worker?worker&url'

构建优化

自动优化

Vite 在构建时自动应用以下优化:

  1. CSS 代码分割:异步 chunk 中的 CSS 会被提取为单独文件
  2. 预加载指令:自动生成 <link rel="modulepreload">
  3. 异步 chunk 加载优化:预加载共享 chunk,减少网络往返

代码分割

手动控制代码分割:

// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// 将 vue 相关库打包在一起
vendor: ['vue', 'vue-router', 'pinia'],
// 按路由分割
'route-user': ['./src/views/User.vue'],
},
},
},
},
})

动态导入分割

// 路由懒加载
const User = () => import('./views/User.vue')

// 组件懒加载
const AsyncModal = defineAsyncComponent(() =>
import('./components/Modal.vue')
)

小结

本章我们学习了 Vite 的核心功能:

  1. HMR:极速的热模块替换,保留应用状态
  2. TypeScript:原生支持,仅转译不类型检查
  3. CSS:预处理器、CSS Modules、PostCSS 开箱即用
  4. 静态资源:智能处理、内联优化、public 目录
  5. Glob 导入:批量导入模块的强大功能
  6. Web Workers:多种方式创建 Worker
  7. 构建优化:自动代码分割和预加载

这些核心功能使 Vite 成为一个强大而灵活的构建工具,能够处理现代 Web 开发的各种需求。