<div style="display: none;" hidden="true" aria-hidden="true" data-nosnippet>Are you an LLM? You can read better optimized documentation at /pages/20260629180000.md for this page in Markdown format</div>
背景
个人知识库用 VitePress 2 构建,Markdown 与主题配置在源码仓维护,构建产物落在 docs/.vitepress/dist。早期走 GitHub Pages(deploy.sh 把 dist force push 到公开 Pages 仓),后来希望:
- 全球 CDN 加速、自定义域名在 DNS 侧统一管理;
- 本地或 CI 一条命令上传静态文件,不必每次 git push 到 Pages 仓;
- 功能分支先上 Preview,合并生产分支后再切 Production。
本文记录用 Cloudflare Pages + wrangler CLI 直传 dist 的完整流程,以及「部署成功却是 Preview 地址」的分支机制说明。示例域名、项目名均已脱敏,可按自己的环境替换。
整体链路
源码仓(Markdown + VitePress 配置 + 主题包)
│ pnpm run build
▼
docs/.vitepress/dist/ ← 纯静态 HTML / JS / CSS
│ wrangler pages deploy
▼
Cloudflare Pages 项目(例如 my-blog)
│ 自定义域名 CNAME
▼
https://blog.example.com与 GitHub Pages 的差异:Pages 仓可以不再作为发布媒介,wrangler 直接把目录上传到 Cloudflare;源码仓仍可保持私有。
前置条件
| 项 | 说明 |
|---|---|
| Cloudflare 账号 | 已添加站点域名,或在 Pages 里绑定自定义域 |
| Node.js + pnpm | 与本地 VitePress 构建一致 |
| wrangler | 项目 devDependencies 安装,或 npx wrangler |
API Token 或 wrangler login | 非交互环境(CI、脚本)必须用 Token |
仓库根目录建议保留 wrangler.toml,与构建输出目录对齐:
name = "my-blog"
pages_build_output_dir = "docs/.vitepress/dist"
compatibility_date = "2024-09-23"name 为默认项目标识;实际部署时可用环境变量覆盖项目名(见下文脚本)。
创建 API Token(Dashboard)
本地脚本、CI 里无法弹出浏览器登录时,需要在 Cloudflare 控制台创建 API Token:
- 打开 Cloudflare Dashboard → 右上角头像 → My Profile → API Tokens。
- Create Token → 可选用模板 Edit Cloudflare Workers(含 Pages 上传权限),或自定义 Token。
- 自定义时至少勾选:
- Account → Cloudflare Pages → Edit
- 若项目绑在指定账号下,把 Account Resources 限定到该账号。
- 创建后只显示一次完整 Token,复制保存;勿写入 Git。
使用方式(当前 shell 有效):
export CLOUDFLARE_API_TOKEN="你的Token"CI 里放进密钥库(GitHub Actions secrets、Jenkins credentials 等),变量名保持一致即可。
验证(可选):
npx wrangler whoami本地构建与首次部署
pnpm install
pnpm run build确认 docs/.vitepress/dist/index.html 存在后:
export CLOUDFLARE_API_TOKEN="${CLOUDFLARE_API_TOKEN}"
npx wrangler pages deploy docs/.vitepress/dist \
--project-name=my-blog- 项目不存在时,wrangler 会提示创建(需 Token 有 Pages Edit 权限)。
- 成功后终端会输出 Preview URL,形如
https://<hash>.my-blog.pages.dev。
Preview 与 Production:为什么域名没变?
这是最容易踩的坑:wrangler pages deploy 默认把本次上传关联到「当前 git 分支」对应的 Preview 部署,不会自动替换 Production。
| 现象 | 原因 |
|---|---|
| 终端显示 Preview URL | 当前分支 ≠ Pages 控制台里的 Production branch |
blog.example.com 仍是旧内容 | 自定义域挂在 Production 部署上,Preview 只有 *.pages.dev |
| 分支名出现在预览域 | 例如 feat-xxx.my-blog.pages.dev |
Production 部署需要显式指定与控制台一致的生产分支,例如 main:
npx wrangler pages deploy docs/.vitepress/dist \
--project-name=my-blog \
--branch=main或在 Cloudflare Dashboard → Workers & Pages → 项目 → Deployments 里,将某次 Preview Promote to production。
请在 Pages 项目 Settings → Builds & deployments 确认 Production branch(常见为 main 或 master),与 --branch 保持一致。
封装部署脚本
为避免每次手写参数,可在仓库根目录增加 deploy-cloudflare.sh,用环境变量区分预览与生产:
#!/usr/bin/env sh
set -e
PROJECT="${CLOUDFLARE_PAGES_PROJECT:-my-blog}"
ENV="${CLOUDFLARE_ENV:-preview}"
PRODUCTION_BRANCH="${CLOUDFLARE_PAGES_PRODUCTION_BRANCH:-main}"
DIST_DIR="docs/.vitepress/dist"
case "$ENV" in
production | prod)
BRANCH="${CLOUDFLARE_PAGES_BRANCH:-$PRODUCTION_BRANCH}"
;;
preview | pre)
if [ -n "${CLOUDFLARE_PAGES_BRANCH:-}" ]; then
BRANCH="$CLOUDFLARE_PAGES_BRANCH"
elif [ -n "${CLOUDFLARE_PAGES_PREVIEW_BRANCH:-}" ]; then
BRANCH="$CLOUDFLARE_PAGES_PREVIEW_BRANCH"
else
BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo preview)"
fi
;;
*)
echo "error: CLOUDFLARE_ENV must be preview or production" >&2
exit 1
;;
esac
pnpm run build
exec wrangler pages deploy "$DIST_DIR" \
--project-name="$PROJECT" \
--branch="$BRANCH"package.json 可挂快捷命令:
{
"scripts": {
"deploy:cloudflare:preview": "CLOUDFLARE_ENV=preview bash deploy-cloudflare.sh",
"deploy:cloudflare:production": "CLOUDFLARE_ENV=production bash deploy-cloudflare.sh"
}
}典型用法:
# 当前功能分支 → Preview
export CLOUDFLARE_API_TOKEN="..."
pnpm run deploy:cloudflare:preview
# 上线生产域
pnpm run deploy:cloudflare:production
# 等价于 --branch=main(或你设置的 PRODUCTION_BRANCH)自定义域名
- Pages 项目 → Custom domains → 添加
blog.example.com/example.com。 - 按提示在 DNS 添加 CNAME 到
my-blog.pages.dev(或 Cloudflare 托管域名时一键配置)。 - 确保 Production 部署已更新;Preview 不会自动绑生产域。
若同时保留 GitHub Pages,注意同一域名只能指向一处,避免 CNAME 冲突。
与 GitHub Pages 脚本并存
很多站点会保留原有 deploy.sh(构建 + push 到 username.github.io),Cloudflare 作为另一条发布通道:
| 通道 | 适用场景 |
|---|---|
| GitHub Pages | 习惯 git 驱动、开源静态仓 |
| Cloudflare Pages | 要 CDN、Preview 分支、不暴露 Pages 仓 |
两者构建命令相同,都是 pnpm run build;仅上传目标不同。评论、统计等前端配置(如 Giscus)与托管商无关,见 VitePress 集成 Giscus 评论实践。
构建失败要先修再部署
部署前务必保证 pnpm run build 通过。常见阻塞项:
- 资源 404:Markdown 引用的图片路径大小写或后缀与
docs/public不一致。 - 本地搜索 duplicate ID:重复
permalink或同名冲突文件(如同步盘产生的*DownloadConflict*副本)导致 MiniSearch 报 duplicate id。
构建失败时 wrangler 不会上传;即使上传了错误产物,Preview 上也会直接 404。
安全与运维建议
- Token 最小权限:仅 Pages Edit + 必要 Account 范围;勿用 Global API Key。
- 勿提交 Token:只通过环境变量或 CI Secret 注入;若误贴到日志,立即在 Dashboard Roll 轮换。
- 生产发布:合并到生产分支后再跑
deploy:cloudflare:production,或在 Dashboard Promote。 - 构建与上传分离:CI 可先
pnpm run build缓存dist,再单独 job 执行wrangler pages deploy,缩短反馈时间。
小结
- VitePress 产物目录固定为
docs/.vitepress/dist,与wrangler.toml的pages_build_output_dir对齐。 - 非交互环境配置
CLOUDFLARE_API_TOKEN,用wrangler pages deploy <dir> --project-name=...直传。 - 不带
--branch或分支名不等于 Production branch 时,只会更新 Preview;生产域需--branch=main或 Dashboard 提升。 - 用
CLOUDFLARE_ENV=preview|production封装脚本,避免功能分支误触生产。
本文为个人静态站实践脱敏整理,文中域名、项目名、路径均为示例,请按自己的环境替换。
