用 Vite 做一个习惯打卡单页应用,连接 GitHub 后让 Vercel 自动部署:每个分支、每个拉取请求都有自己的预览网址,合并到 main 才更新正式站。页面底部显示当前是 preview 还是 production,刷新任何子页面都不会 404。
先看清成品
成品是一个 Vite 项目。本文已用 Vite 8.3.4 实际构建:设置
VITE_VERCEL_ENV=preview构建后,产物里确实写入了preview;vite preview本地预览时直接访问/stats返回 200。本文环境没有 Vercel 和 GitHub 账号,没有实际连接仓库,也没有生成预览部署。
注意:Vercel 的免费 Hobby 计划仅限个人、非商业用途(官方 Hobby 页面和公平使用准则写明),商业项目需要 Pro 计划。各计划额度以官方页面为准。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
| 打卡应用 | 两个页面:今天、统计,数据存在浏览器 |
| 前端路由 | History API,网址是 /stats 而不是 #/stats |
| SPA 回退 | vercel.json 把所有路径重写到 index.html |
| 预览部署 | 每个分支和拉取请求一个网址 |
| 环境标识 | 页面显示 preview / production |
需要准备哪些环境?
| 项目 | 要求 | 说明 |
|---|---|---|
| Vercel 账号 | Hobby 免费计划即可 | 用 GitHub 账号注册最方便 |
| Node.js | 20 或更高 | 本文使用 22.23.3 |
| Vercel CLI | npm i -g vercel |
本文核对时最新版为 65.0.0 |
| GitHub 仓库 | 可选 | 连接后每次推送自动部署 |
第一步:怎样建项目?
1. 项目结构
habit-spa/
├── index.html
├── src/main.js
├── vercel.json
├── package.json
└── .gitignore
2. package.json
{
"name": "habit-spa",
"private": true,
"type": "module",
"scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" },
"devDependencies": { "vite": "^8.3.4" }
}
3. index.html
<!doctype html>
<html lang="zh-CN">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>习惯打卡</title></head>
<body>
<nav><a href="/" data-link>今天</a> · <a href="/stats" data-link>统计</a></nav>
<main id="app"></main>
<footer id="env"></footer>
<script type="module" src="/src/main.js"></script>
</body>
</html>
4. src/main.js
// 单页应用:用 History API 切换页面,数据存 localStorage
const KEY = 'habits-v1';
const HABITS = ['喝水 8 杯', '运动 30 分钟', '读书 20 页'];
const today = () => new Date().toLocaleDateString('sv'); // YYYY-MM-DD
const load = () => JSON.parse(localStorage.getItem(KEY) || '{}');
const save = (d) => localStorage.setItem(KEY, JSON.stringify(d));
function todayPage() {
const d = load(); const done = d[today()] || [];
return `<h1>今天 ${today()}</h1>` + HABITS.map((h) =>
`<label><input type="checkbox" data-h="${h}" ${done.includes(h) ? 'checked' : ''}> ${h}</label><br>`).join('');
}
function statsPage() {
const d = load();
const rows = HABITS.map((h) => `<li>${h}:${Object.values(d).filter((x) => x.includes(h)).length} 天</li>`).join('');
return `<h1>累计打卡</h1><ul>${rows}</ul>`;
}
const routes = { '/': todayPage, '/stats': statsPage };
function render() {
const page = routes[location.pathname] || (() => '<h1>页面不存在</h1>');
document.getElementById('app').innerHTML = page();
}
document.addEventListener('click', (e) => {
const a = e.target.closest('a[data-link]');
if (a) { e.preventDefault(); history.pushState(null, '', a.href); render(); }
});
document.addEventListener('change', (e) => {
const h = e.target.dataset.h; if (!h) return;
const d = load(); const set = new Set(d[today()] || []);
e.target.checked ? set.add(h) : set.delete(h);
d[today()] = [...set]; save(d);
});
window.addEventListener('popstate', render);
// 构建时注入的环境标识:预览部署和正式部署显示不同
document.getElementById('env').textContent = `环境:${import.meta.env.VITE_VERCEL_ENV ?? 'local'}`;
render();
| 要点 | 说明 |
|---|---|
VITE_VERCEL_ENV |
Vercel 文档说明,系统环境变量加上 VITE_ 前缀即可在 Vite 构建时读取,值为 preview、production 或 development |
toLocaleDateString('sv') |
得到 2026-10-21 这样的本地日期 |
data-link |
拦截站内链接,不刷新页面 |
5. vercel.json
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
没有这个重写,直接打开或刷新 https://…/stats 会 404,因为服务器上没有 stats 这个文件。
6. .gitignore
node_modules
dist
.vercel
.env*.local
第二步:怎样本地运行?
npm install
npm run dev
打开终端显示的地址,勾选几项,切到“统计”页确认天数增加。然后验证构建:
VITE_VERCEL_ENV=preview npm run build
npx vite preview
本文实际构建输出:
dist/index.html 0.40 kB │ gzip: 0.31 kB
dist/assets/index-BtSGPHD1.js 1.76 kB │ gzip: 1.04 kB
✓ built in 42ms
第三步:怎样接入 GitHub 自动部署?
1. 推送到 GitHub
git init && git add . && git commit -m "init"
git branch -M main
git remote add origin git@github.com:你的用户名/habit-spa.git
git push -u origin main
2. 在 Vercel 导入
Vercel 控制台 Add New → Project,选择这个仓库。框架会自动识别为 Vite,构建命令 npm run build、输出目录 dist,保持默认,点 Deploy。
3. 体验预览部署
git checkout -b dark-mode
# 随便改一处文字
git commit -am "试试新标题" && git push -u origin dark-mode
在 GitHub 上为这个分支开拉取请求,Vercel 机器人会在评论里贴出预览网址。打开后页面底部显示 环境:preview;合并到 main 后,正式网址显示 环境:production。
4. 按环境设置不同的变量
如果以后要接后端,可以给不同环境配置不同地址:
vercel env add VITE_API_BASE preview
vercel env add VITE_API_BASE production
注意:所有
VITE_开头的变量都会被打包进前端代码,任何人都能看到,不要放密钥。
出问题时该怎么调?
1. 刷新子页面 404
确认 vercel.json 在项目根目录并且已提交。
2. 页面显示“环境:local”
线上出现这个值,说明构建时没读到变量。到部署详情的构建日志确认使用的是 Vite 框架预设;改了环境变量后需要重新部署。
3. 构建失败
本地先 npm run build。常见原因是 Node 版本太旧,可在项目 Settings → Build and Deployment 选择 Node 版本。
常见问题怎么处理?
| 现象 | 原因 | 解决方法 |
|---|---|---|
| 预览网址别人打不开 | 开启了部署保护 | 在项目设置里调整,或登录后访问 |
| 数据换浏览器就没了 | 存在 localStorage | 需要同步就接入后端 |
| 拉取请求没有预览评论 | 没装 Vercel GitHub 应用权限 | 在 GitHub 设置里授权该仓库 |
| 资源 404 | 改了 base 路径 |
部署在根路径时保持默认 |
| 想回滚 | 新版本有问题 | Deployments 里对旧版本使用 Instant Rollback |
项目资源
| 用途 | 地址 |
|---|---|
| Vite on Vercel | https://vercel.com/docs/frameworks/frontend/vite |
| 部署环境 | https://vercel.com/docs/deployments/environments |
| Git 集成 | https://vercel.com/docs/git |
| vercel env 命令 | https://vercel.com/docs/cli/env |
| Vite 环境变量 | https://vite.dev/guide/env-and-mode |
| vercel.json 配置 | https://vercel.com/docs/project-configuration |
建议先在本地跑通两个页面,再推送 main 完成第一次部署。
然后专门开一个分支练习“改动 → 预览 → 合并”的流程,熟悉之后所有项目都可以按这个方式上线。