部署指南
本页介绍如何将基于 vitepress-theme-ninc 构建的博客部署到 Vercel、Netlify、GitHub Pages、阿里云 ESA 或自有服务器(Nginx)。同时涵盖 PWA 缓存、自定义域名与环境变量等部署期注意事项。
构建命令
无论选择哪种部署平台,构建产物都由 VitePress 生成,命令与产物目录是统一的:
| 命令 | 作用 |
|---|---|
pnpm install | 安装依赖(含主题的 postinstall 自动应用 nes-vue 补丁) |
pnpm build | 构建静态站点,产物输出到 .vitepress/dist |
pnpm preview | 本地预览构建产物 |
确认 package.json 脚本
确保项目 package.json 的 scripts 中包含 "build": "vitepress build"。若站点根目录就是项目根目录,默认产物路径为 .vitepress/dist;若你自定义了 VitePress 的 outDir,请相应调整部署平台的发布目录。
本地完整验证一次构建,可避免大部分部署失败:
pnpm install
pnpm build
pnpm preview部署到 Vercel
Vercel 原生支持 VitePress 项目,可自动识别框架。若自动识别失败或需要精细控制,可在项目根目录新建 vercel.json:
{
"buildCommand": "pnpm build",
"outputDirectory": ".vitepress/dist",
"installCommand": "pnpm install",
"framework": null
}Node 版本
Vercel 默认 Node 版本可能低于主题要求的 >= 20。请在项目根目录添加 .nvmrc 文件,内容为 20,或在 Vercel 项目设置 → General → Node.js Version 中选择 20.x。
Monorepo 部署
若你的项目是 monorepo(主题仓库的 blog/ 或 docs/ 作为子目录部署),需要在 Vercel 项目设置中:
- 将 Root Directory 设置为
blog/(或docs/)。 - 确保
vercel.json位于该子目录下,outputDirectory相对该子目录解析。
部署到 Netlify
在项目根目录新建 netlify.toml:
[build]
command = "pnpm build"
publish = ".vitepress/dist"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200SPA 回退
[[redirects]] 段把所有未命中静态资源的请求回退到 index.html,避免刷新动态路由(如 /page/2、/pages/categories/xxx)时出现 404。VitePress 默认开启 cleanUrls,配合此重定向可保证路由可用。
Monorepo 部署
在 Netlify 项目设置中:
- Base directory 设置为
blog/(或docs/)。 - Build command 保持
pnpm build。 - Publish directory 设置为
.vitepress/dist(相对 base directory)。
Node 版本
在 netlify.toml 中或站点设置里指定 Node 版本:
[build.environment]
NODE_VERSION = "20"部署到 GitHub Pages
GitHub Pages 免费托管静态站点,配合 GitHub Actions 可实现推送后自动构建部署。
Step 1:设置 base 路径
部署在子路径的项目站(用户名.github.io/仓库名/)必须设置 base,否则样式与资源全部 404。本主题推荐在 themeConfig.siteMeta.base 中设置——主题会把它自动同步为 VitePress base 与 PWA start_url,一处配置两处生效:
// .vitepress/themeConfig.ts
siteMeta: {
base: '/仓库名/', // 项目站必填;个人站(仓库名为 用户名.github.io)用 '/' 即可
// ...
}你也可以改在 defineConfig 第一参数中设置 base: '/仓库名/'(VitePress 原生方式,会覆盖 siteMeta.base 的同步值),但此时需把 siteMeta.base 改为相同值,否则 PWA start_url 与站点实际路径不一致。
Step 2:创建 GitHub Actions 工作流
在项目根目录新建 .github/workflows/deploy.yml:
name: Deploy to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v6
with:
node-version: 20
cache: pnpm
- uses: actions/configure-pages@v4
- run: pnpm install
- run: pnpm build
- uses: actions/upload-pages-artifact@v3
with:
path: .vitepress/dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4Step 3:开启 Pages
仓库 Settings → Pages → Source 选择 GitHub Actions(不要选 Deploy from a branch),推送 main 分支即自动部署。
动态路由无需 SPA 回退
VitePress 是 SSG——/page/2、/pages/categories/xxx 等路由在构建时都生成了真实的 HTML 文件,GitHub Pages 可直接命中,不存在刷新 404 问题。未命中的路径会由 VitePress 构建出的 404.html 接管。
部署到阿里云 ESA
阿里云 ESA(边缘安全加速)的 Pages 功能提供静态站点托管,国内访问速度优于 GitHub Pages,免费套餐含不限量流量(边缘函数另有每日 10 万次请求额度)。
Step 1:添加 esa.jsonc
在项目根目录新建 esa.jsonc(该文件的配置优先级高于控制台,存在时控制台对应配置不生效):
{
"name": "my-blog",
"installCommand": "pnpm install",
"buildCommand": "pnpm run build",
"assets": {
"directory": "./.vitepress/dist"
}
}notFoundStrategy 通常不需要
与 GitHub Pages 同理,VitePress 的每个路由都有真实 HTML 文件,默认路由模式即可命中。若希望未命中路径返回 404.html 及 404 状态码,可追加 "notFoundStrategy": "404Page"。
Step 2:控制台导入仓库
- 登录 ESA 控制台,进入 边缘计算和 AI → 函数和 Pages,点击 创建。
- 选择 导入 GitHub 仓库 并完成授权,选中你的博客仓库。
- 确认构建信息(
esa.jsonc存在时以其为准),点击 开始部署。
Node 版本
ESA 构建的 Node 版本可在控制台「高级配置」中指定;项目 package.json 的 engines.node 声明(主题要求 >= 20)优先级更高,建议保留该声明。
Monorepo 部署
在控制台「高级配置 → Root Directory」中填写子目录(如 /blog),构建命令将在该目录下执行。
部署到自有服务器(Nginx)
Step 1:构建产物
在本地或 CI 中执行构建,并将 .vitepress/dist 目录上传到服务器,例如 /var/www/my-blog:
pnpm install
pnpm build
# 上传到服务器
rsync -avz --delete .vitepress/dist/ user@your-server:/var/www/my-blog/Step 2:Nginx 配置
server {
listen 80;
server_name xxx.xxx.com;
root /var/www/my-blog;
index index.html;
# gzip(主题构建已产出 .gz,可直接启用 gzip_static)
gzip_static on;
# brotli(若已安装 ngx_brotli 模块)
# brotli_static on;
# SPA 回退:动态路由刷新不报 404
location / {
try_files $uri $uri/ $uri.html /index.html;
}
# 静态资源长缓存
location ~* \.(js|css|png|jpg|jpeg|gif|webp|svg|woff2?|ttf)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML 不缓存,保证更新即时生效
location ~* \.html$ {
add_header Cache-Control "no-cache";
}
# service worker 不缓存自身
location = /sw.js {
add_header Cache-Control "no-cache";
}
location = /registerSW.js {
add_header Cache-Control "no-cache";
}
}配置完成后重载 Nginx:
sudo nginx -t
sudo nginx -s reloadHTTPS
建议使用 Certbot 申请 Let's Encrypt 证书,将 listen 80 改为 listen 443 ssl 并配置证书路径,PWA 与 Service Worker 均要求 HTTPS 环境。
PWA 注意事项
主题通过 @vite-pwa/vitepress 提供 PWA 能力,构建产物包含 sw.js、registerSW.js、manifest.webmanifest 等。部署时需注意:
- 必须 HTTPS:Service Worker 只能在 HTTPS(或
localhost)下注册,HTTP 站点会静默失败。 - sw.js 不缓存自身:如上方 Nginx 配置所示,
sw.js与registerSW.js必须设置no-cache,否则用户更新站点后无法拿到新的 Service Worker,导致缓存陈旧。 - 预缓存策略:主题默认预缓存构建产物。若产物体积较大,可通过
defineConfig第三参数的pwaWorkbox自定义运行时缓存规则,详见 配置详解 - PWA 降级机制。 - 关闭 PWA:若不需要 PWA,可在
defineConfig第三参数设置pwa: false,构建时不会生成 Service Worker,相关文件也无需部署。
更新不生效?
PWA 站点更新后用户可能仍看到旧内容,这是 Service Worker 预缓存的表现。开发期可在浏览器 DevTools → Application → Service Workers 勾选「Update on reload」;生产环境确保 sw.js 不被 CDN/浏览器强缓存即可。
自定义域名
Vercel
在项目设置 → Domains 中添加自定义域名,按提示在你的域名服务商添加 CNAME(或 A)记录。Vercel 会自动签发并续期 HTTPS 证书。
Netlify
在站点设置 → Domain management → Add custom domain 中添加域名,并按提示配置 DNS。Netlify 同样自动签发 Let's Encrypt 证书。
自有服务器
在域名服务商将 xxx.xxx.com 解析到服务器 IP,Nginx 配置 server_name xxx.xxx.com 并配合 Certbot 签发证书。
GitHub Pages
仓库 Settings → Pages → Custom domain 填入域名,并在域名服务商添加 CNAME 记录指向 用户名.github.io。使用自定义域名后 base 应改回 '/'。
阿里云 ESA
在 ESA 项目的域名管理中添加自定义域名并按提示配置 DNS 解析,ESA 自动签发 HTTPS 证书。
配置站点地址
无论使用哪种平台,请确保 themeConfig.siteMeta.site 与 defineConfig 中 sitemap.hostname 都填写为最终线上域名(如 https://xxx.xxx.com),以保证 RSS、sitemap、Open Graph 等链接正确。
环境变量
部分主题功能涉及密钥(Twikoo 评论、Algolia 搜索、统计代码等),不要把这些密钥硬编码提交到仓库。推荐使用环境变量注入。
在 themeConfig 中读取环境变量
// themeConfig.ts
import { defineThemeConfig } from 'vitepress-theme-ninc/defineThemeConfig'
export const themeConfig = defineThemeConfig({
comment: {
twikoo: {
envId: process.env.TWIKOO_ENV_ID || ''
}
},
search: {
enable: !!process.env.ALGOLIA_APP_ID,
appId: process.env.ALGOLIA_APP_ID || '',
apiKey: process.env.ALGOLIA_API_KEY || '',
indexName: process.env.ALGOLIA_INDEX_NAME || ''
},
tongji: {
'51la': process.env.LA_ID_51 || ''
}
})各平台配置环境变量
| 平台 | 配置位置 |
|---|---|
| Vercel | 项目设置 → Environment Variables |
| Netlify | 站点设置 → Environment variables(注意:构建期变量需在 netlify.toml 的 [build.environment] 中声明) |
| 自有服务器 / CI | 在构建脚本前 export TWIKOO_ENV_ID=xxx,或使用 .env 文件配合 dotenv |
构建期 vs 运行期
VitePress 是构建期注入配置,环境变量在 pnpm build 时被读取并写死到产物中。因此所有密钥都必须在构建环境的变量中可用,运行期的服务器环境变量不会生效。
AI 摘要的部署要点
开启了 aiSummary AI 文章摘要 的站点,除配置大模型的 apiKey 环境变量外,建议将缓存文件 .vitepress/ai-summary-cache.json 提交到仓库,CI 构建时可直接复用缓存,避免对全部文章重复调用大模型。详细步骤见该页的「第五步:部署时做什么」。
不要提交 .env
将 .env 加入 .gitignore,仅保留 .env.example 作为模板提交到仓库,避免密钥泄露。
部署检查清单
部署前请逐项确认:
- 本地
pnpm build成功,无报错 .vitepress/dist中包含rss.xml、sitemap.xml(若启用)themeConfig.siteMeta.site与sitemap.hostname为线上域名- Node 版本
>= 20 - HTTPS 已启用(PWA 必需)
sw.js未被强缓存- 敏感密钥通过环境变量注入,未硬编码
- 动态路由刷新不报 404(Vercel/Netlify/Nginx 需配置 SPA 回退;GitHub Pages/ESA 每个路由均有真实 HTML,无需回退)