讲解

开发时 Vite 即时编译源码,上线跑 npm run build:JSX 编译、Tree-shaking、压缩混淆、代码分割,产物是 dist/ 下一批带内容哈希的静态文件。内容哈希意味着文件内容变文件名才变,于是带哈希的资源可以配「永久缓存」,index.html 配短缓存或不缓存——用户只在发版时下载变化的文件。

部署是「把静态文件交给 Web 服务器」的问题,选择很多:静态托管平台(Vercel/Netlify/Cloudflare Pages,推送代码自动构建部署,最省心)、对象存储 + CDN、Nginx 容器。共同的必配项是 SPA 回退:BrowserRouter 的 URL(/user/42)在服务器上并不存在对应文件,所有未匹配路径要返回 index.html,否则刷新就 404。Nginx 里 try_files $uri $uri/ /index.html;,托管平台一般有一键的 SPA rewrite 配置。

环境变量:.env 文件里 VITE_ 开头的变量(VITE_API_BASE=...)在构建时被内联进代码,代码里用 import.meta.env.VITE_API_BASE 访问。两个推论:改环境变量必须重新构建;变量值会进入产物对所有人可见——永远不要放密钥,密钥属于后端。

上线 checklist:本地 npm run preview 验一遍产物(dev 服务器和产物行为可能不同);确认回退配置(直接访问子路由测试);sourcemap 策略(排错有用,注意暴露面);CI 里先跑 lint + test + build 再部署;回滚方案(静态站回滚 = 指回上一版产物,秒级)。

示例

验证内容哈希缓存策略与 Tree-shaking 的基本原理(在本教程构建时被真实执行):

import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';

// 内容哈希命名:内容变 → 文件名变 → 缓存自动失效
function bundle(name, content) {
  const hash = createHash('sha256').update(content).digest('hex').slice(0, 8);
  return name + '.' + hash + '.js';
}
const a1 = bundle('index', 'console.log("v1")');
const a2 = bundle('index', 'console.log("v2")');
assert.notStrictEqual(a1, a2);
assert.strictEqual(bundle('index', 'console.log("v1")'), a1); // 内容不变名字不变

// 模拟 tree-shaking:只保留被引用的导出
function treeShake(modules, entryImports) {
  const used = new Set();
  for (const imp of entryImports) used.add(imp);
  return modules.filter((m) => used.has(m.name));
}
const lib = [
  { name: 'debounce', size: 2 },
  { name: 'throttle', size: 2 },
  { name: 'cloneDeep', size: 30 },
  { name: 'everyOtherUtil', size: 500 },
];
const shipped = treeShake(lib, ['debounce']); // 只用了 debounce
assert.deepStrictEqual(
  shipped.map((m) => m.name),
  ['debounce'],
);
const saved = lib.reduce((s, m) => s + m.size, 0) - shipped[0].size;
assert.strictEqual(saved, 532);

console.log('产物:', a1, '→', a2, '(内容驱动文件名)');
console.log('tree-shaking: 534KB 库只打包用到的', shipped[0].size, 'KB');

典型的部署配置(仅示意):

npm run build
rsync -av dist/ server:/var/www/my-app/

# Nginx 要点:
# location / { try_files $uri $uri/ /index.html; }  # SPA 回退
# location /assets/ { expires 1y; add_header Cache-Control immutable; }
# .env.production —— 只放公开值,密钥永远在后端
VITE_API_BASE=https://api.example.com

常见坑

  • 忘配 SPA 回退:首页能开、刷新子路由 404;任何部署方式都要配 rewrite 到 index.html。
  • index.html 长缓存:用户拿到旧入口引旧哈希文件,发版失效;入口文件 no-cache。
  • .env 放密钥:VITE_ 变量编译进产物,公开可见;密钥只存在于后端环境。
  • 改 .env 后没重新 build:编译期内联,热更新都不生效,必须重新构建。
  • 构建警告视而不见:chunk 过大警告、循环依赖警告都是线上问题的前兆,发版前清零。

小结

build 出带哈希的静态产物;SPA 回退必配;VITE_ 变量编译期内联、禁放密钥;preview 验产物、CI 把关、静态站回滚秒级。24 章到此完整——从 createElement 到生产部署,接下来动手写一个完整项目是最好的消化。