讲解

开发时跑的是 Vite 的即时编译,上线要跑 npm run build:产出压缩、混淆、带内容哈希文件名的静态文件到 dist/ 目录。内容哈希是关键设计——文件内容变了文件名就变,于是可以放心给静态资源配「永久缓存」HTTP 头,用户只在发版时下载变化的文件。

构建产物是纯粹的静态文件,意味着部署选择极多:对象存储 + CDN(最便宜)、静态托管平台(Netlify/Vercel/Cloudflare Pages,推送代码自动构建)、Nginx 容器。共同的要点是 history 路由的回退配置:所有未匹配的路径都要返回 index.html,否则用户刷新 /user/42 会 404。Nginx 里就是 try_files $uri $uri/ /index.html;

环境变量在构建时注入:以 VITE_ 开头的变量(.env 文件里 VITE_API_BASE=https://api.example.com)会被编译进代码,通过 import.meta.env.VITE_API_BASE 访问。注意它们是编译期替换的——改环境变量必须重新构建,而且变量会进产物,绝不放密钥(密钥属于后端)。

上线前的 checklist:构建无警告;产物用 npm run preview 本地验一遍;sourcemap 按需生成(排查线上问题有用,但别公开给所有人);配好 CDN 缓存策略(index.html 不缓存或短缓存,带哈希的静态资源长缓存);灰度/回滚方案(静态站天然简单——保留上一个版本的产物目录即可秒级回滚)。

示例

用 JS 算一笔缓存账:内容哈希为什么能让静态资源永久缓存(在本教程构建时被真实执行):

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

// 模拟 Vite 的产物命名:内容哈希进文件名
function bundle(name, content) {
  const hash = createHash('sha256').update(content).digest('hex').slice(0, 8);
  return name + '.' + hash + '.js';
}

const v1 = bundle('index', 'console.log("hello")');
const v2 = bundle('index', 'console.log("hello world")');
const v1again = bundle('index', 'console.log("hello")');

assert.notStrictEqual(v1, v2); // 内容变 → 文件名变 → 缓存自动失效
assert.strictEqual(v1, v1again); // 内容不变 → 文件名不变 → 命中缓存

// 环境变量编译期注入的模拟
function buildWithEnv(env, code) {
  return code.replaceAll('import.meta.env.VITE_API_BASE', JSON.stringify(env.VITE_API_BASE));
}
const devCode = buildWithEnv({ VITE_API_BASE: 'http://localhost:3000' }, 'fetch(import.meta.env.VITE_API_BASE)');
const prodCode = buildWithEnv({ VITE_API_BASE: 'https://api.example.com' }, 'fetch(import.meta.env.VITE_API_BASE)');
assert.ok(devCode.includes('http://localhost:3000'));
assert.ok(prodCode.includes('https://api.example.com'));

console.log('产物文件名:', v1, '→ 改代码后', v2);
console.log('dev 产物内嵌:', devCode);
console.log('环境变量是编译期替换:改 .env 必须重新 build,且产物可见(禁放密钥)');

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

# 构建并上传
npm run build
rsync -av dist/ server:/var/www/my-app/

# Nginx 配置要点:
# location / { try_files $uri $uri/ /index.html; }   # history 路由回退
# location /assets/ { expires 1y; }                   # 带哈希的资源永久缓存
# .env.production —— 只放公开值
VITE_API_BASE=https://api.example.com

常见坑

  • history 模式忘配回退:首页能开、刷新子路由 404;Nginx/托管平台都要配 SPA fallback。
  • index.html 被长缓存:用户永远拿到旧入口、旧哈希文件名,发版等于白发;index.html 要 no-cache 或极短缓存。
  • .env 里放密钥:VITE_ 变量会编进产物,任何人可见;密钥只能待在后端。
  • 改了 .env 没重新构建:编译期注入,dev server 都要重启才生效。
  • dist 直接部署不开 gzip/brotli:文本资源压缩后体积小一个量级,CDN/托管平台大多自动开,自建 Nginx 要手动配。

小结

build 出带内容哈希的静态产物;index.html 短缓存、哈希资源永久缓存;history 模式配回退;VITE_ 变量编译期注入、禁放密钥。到这里,24 章的 Vue 3 之旅就完整了——从响应式原理到工程化交付,接下来最好的学习就是动手写一个完整项目。