本文记录 JavaGuide 当前的性能现状、CDN/Nginx 配置约定、已经完成的优化和后续待讨论事项。
- 电脑端卡顿不像是单纯 CDN 问题,更偏向客户端渲染、解析和执行成本高。
- 老款 Intel Mac 卡、M 系列 Mac 流畅,符合“下载不慢,但老 CPU 处理页面吃力”的特征。
- 重点问题集中在大站点客户端搜索索引、Mermaid 图表、长文页面 DOM/代码块、全站客户端组件初始化。
腾讯云 EdgeOne 已按以下思路调整:
- HTML 不缓存,避免发布后用户拿旧 HTML 引用新旧不匹配的 JS/CSS。
- hash 静态资源使用长期缓存:
/assets/*.js/assets/*.css- 建议
Cache-Control: public, max-age=31536000, immutable
- 图片资源使用较长缓存:
jpg/jpeg/png/gif/bmp/svg/webp/ico- 建议 30 天缓存。
- EdgeOne 节点缓存 TTL 已调整为遵循源站
Cache-Control。 - EdgeOne 无
Cache-Control头时已调整为不缓存。 - EdgeOne 浏览器缓存 TTL 已设置为遵循源站
Cache-Control。 - HTML 不再使用 CDN stale,发布后新访问应尽快拿到新 HTML。
已验证过的线上响应特征:
- 首页 HTML 使用不缓存策略。
/assets/app-*.js可命中 CDN,且适合一年 immutable。favicon.ico和图片类资源适合 30 天缓存。
后端 Nginx 需要和 CDN 策略保持一致:
- HTML/JSON 不长期缓存。
- hash JS/CSS 使用一年强缓存和
immutable。 - 图片 30 天缓存。
- 开启 gzip;如果环境支持,可在 CDN 层开启 Brotli。
- 静态资源不要设置会破坏 CDN 压缩、转换或缓存的头。
- 扩展名省略的 VuePress 路由,例如
/ai/、/database/mysql/,也要返回 HTML 不缓存头,不能只匹配*.html。
当前站点使用 Vite/VuePress 内容 hash 资源,发布时必须保留旧的 /assets/* 文件一段时间。
原因:
- 已打开页面的 SPA 运行时可能还引用上一版 chunk。
- CDN 或浏览器可能短时间内仍持有旧 HTML。
- 如果部署脚本先执行
rm -rf /www/wwwroot/javaguide.cn/*,旧 hash JS/CSS 会被删除;旧 HTML 或旧客户端再请求这些文件时会 404,表现为动态 import 失败、路由跳转失败或页面白屏。
推荐发布方式:
set -e
SITE_DIR="/www/wwwroot/javaguide.cn"
DIST_DIR="/github/dist"
VERIFY_FILE_GOOGLE="/www/wwwroot/googleca8171acadbdab54.html"
# 如果启用了 IndexNow,把 key 文件放在站点根目录可访问的位置。
# 文件名通常是 ${INDEXNOW_KEY}.txt,文件内容也是 INDEXNOW_KEY。
# 不启用时可以删掉这一行和下面的 cp。
VERIFY_FILE_BING="/www/wwwroot/300af4bf44b34b5daf5182f3f4be2c6f.txt"
mkdir -p "$SITE_DIR/assets"
# HTML、sitemap、manifest 等非 assets 文件跟随新版本删除旧文件。
rsync -av --delete \
--exclude='assets/' \
"$DIST_DIR/" "$SITE_DIR/"
# hash 资源只增量覆盖,不在每次部署时删除旧文件。
rsync -av \
"$DIST_DIR/assets/" "$SITE_DIR/assets/"
# 恢复搜索引擎验证文件和 IndexNow key 文件。
cp "$VERIFY_FILE_GOOGLE" "$SITE_DIR/"
cp "$VERIFY_FILE_BING" "$SITE_DIR/"如果暂时没有启用 IndexNow,可以先删除脚本里的 INDEXNOW_KEY_FILE 和对应 cp。启用后需要确认:
https://javaguide.cn/300af4bf44b34b5daf5182f3f4be2c6f.txt可以访问。https://javaguide.cn/{INDEXNOW_KEY}.txt可以访问。{INDEXNOW_KEY}.txt的文件内容就是INDEXNOW_KEY本身。
部署后 CDN 刷新建议:
- 优先刷新 HTML、sitemap、manifest 等入口文件。
- 不建议每次都刷新整个根目录;如果必须刷新根目录,前提是源站仍保留旧 assets。
- 旧 assets 可用定时任务按 30-60 天清理,避免无限增长。
这部分是实际发布时照着做的简化流程。
pnpm docs:build如果这次改了主题配置、构建插件、搜索配置或怀疑缓存影响构建结果,使用 clean build:
pnpm docs:build:clean按上面的 rsync 方式发布,核心是“非 assets 跟随新版本删除,assets 只增量覆盖”:
- 非 assets 文件使用
--delete,让 HTML、sitemap、robots、manifest 跟随新版本。 /assets/只增量覆盖,不在每次部署时删除旧 hash 文件。- 保留站点验证文件,例如 Google/Bing 的验证文件。
- 如果启用了 IndexNow,保留
{INDEXNOW_KEY}.txt。
部署后优先刷新这些入口文件:
//home.html/sitemap.xml/robots.txt- 这次改动涉及的栏目页和文章页 HTML
不建议每次刷新整个根目录;如果刷新根目录,源站必须仍保留旧 /assets/* 文件。
内容变更或文章结构变更后,从本地 dist 写入 Algolia DocSearch 索引:
DOCSEARCH_APP_ID=XXQ4GI90SC \
DOCSEARCH_INDEX_NAME=javaguide \
DOCSEARCH_SOURCE_DIR=dist \
DOCSEARCH_ADMIN_API_KEY=你的写入索引专用 Key \
pnpm docsearch:index只改样式、缓存、Nginx 或不影响正文内容时,可以不重建搜索索引。
小范围改动优先只提交变更 URL:
INDEXNOW_KEY=300af4bf44b34b5daf5182f3f4be2c6f \
pnpm indexnow:submit /home.html /ai/ /cs-basics/大范围内容更新、导航调整或 sitemap 变化后,可以提交 sitemap 中的全部 URL:
INDEXNOW_KEY=300af4bf44b34b5daf5182f3f4be2c6f \
pnpm indexnow:submit --sitemapINDEXNOW_KEY 不能提交到仓库。线上需要能访问 https://javaguide.cn/{INDEXNOW_KEY}.txt。
curl -I https://javaguide.cn/
curl -I https://javaguide.cn/assets/app-xxx.js
curl -s https://javaguide.cn/robots.txt
curl -s https://javaguide.cn/sitemap.xml | head重点确认:
- HTML 不长期缓存。
- hash JS/CSS 是长期缓存。
robots.txt包含Sitemap: https://javaguide.cn/sitemap.xml。- 重要入口页在 sitemap 中存在。
- 栏目入口页是
weekly,普通文章默认是monthly。
- 站内搜索使用 Algolia DocSearch,不再生成 VuePress 本地客户端搜索索引。
- 主题中保留 DocSearch 配置入口:
DOCSEARCH_APP_IDDOCSEARCH_API_KEYDOCSEARCH_INDEX_NAME
- 没有 DocSearch key 时关闭搜索,避免生成本地
searchIndex.js。 - 当前 Algolia 应用 ID:
XXQ4GI90SC - 当前前端索引名:
javaguide - 前端
DOCSEARCH_API_KEY必须使用XXQ4GI90SC应用下的 Search-Only API Key,不能使用旧应用U3RN7F5WI0的 key。 - 构建环境变量示例:
DOCSEARCH_APP_ID=XXQ4GI90SC
DOCSEARCH_INDEX_NAME=javaguide
DOCSEARCH_API_KEY=3b514f...ef027b- 上面的
DOCSEARCH_API_KEY只记录掩码;实际构建时使用完整 Search-Only API Key。 DOCSEARCH_ADMIN_API_KEY只用于本地/CI 写索引,不能提交到仓库,也不能放到前端环境变量里。- 推荐从本地构建产物
dist生成索引,避免在线抓取受 CDN、反爬、缓存或页面动态渲染影响。 - 2026-05-14 已用本地
dist成功写入javaguide索引,索引 records 约 4.7 万条。
- 全站 sitemap 默认频率是
monthly,匹配普通文章几个月更新一次的实际情况。 - 首页和栏目入口页保留显式
weekly://home.html/ai//cs-basics/
- 普通文章不要单独写
sitemap.changefreq: weekly,除非它确实会频繁更新。 robots.txt由 VuePress SEO/Sitemap 插件生成,并自动追加 sitemap 地址。- 已新增
pnpm indexnow:submit,用于部署后向 Bing/IndexNow 主动提交 URL。 - 重要页面需要重点维护:
title:包含核心搜索词,但不要堆砌。description:说明页面覆盖范围和适用人群。keywords:可作为补充,不要依赖它决定排名。- 首屏正文:广告或提示块前尽量有一段能概括页面价值的真实内容。
- 内链锚文本:使用“Java 面试”“AI 应用开发面试”“计算机基础面试题”等明确词,而不是泛泛的“点击这里”。
- 新增或重写重点栏目后,优先检查:
- 生成后的
dist/sitemap.xml是否包含目标 URL。 changefreq是否符合真实更新节奏。- canonical、Open Graph、JSON-LD 是否生成正常。
- Bing Webmaster Tools / Google Search Console 中是否能正常抓取。
- 生成后的
- 普通页面不再读取
localStorage、查询 DOM、读取scrollHeight或注入样式。 - 只有命中受保护路径时才执行加锁逻辑。
- 从受保护页面切走时清理之前加过的锁样式。
- 新增懒加载 Mermaid 包装组件。
- 页面初始只展示轻量占位。
- 图表接近视口后再加载原 Mermaid 组件和
mermaid.esm.min。 - RocketMQ 页面本地烟测:
- 初始 Mermaid 占位数量为 13。
- 初始 SVG 渲染数量为 0。
- 这说明 Mermaid 不再抢占首屏初始化;滚动触发渲染仍建议在未加锁页面继续定期抽测。
LayoutToggle改为延后到浏览器空闲时加载。UnlockContent保持异步组件注册。GlobalUnlock保持同步,避免受保护内容短暂露出。
- 关闭
photoSwipe图片预览插件。 - 原因:图片点击放大不是文档阅读首屏刚需,但会额外带来初始 JS 请求。
- 如果后续仍需要图片放大能力,建议实现“点击图片后再懒加载预览库”。
- 当前轻量图片预览组件
ClickImagePreview已改为 mounted 后再渲染 Teleport。 - 原因:Teleport 作为 root component 直接参与 SSR hydration 时,会导致 VuePress 首页被水合为空注释,表现为页面白屏且只剩
Hydration completed but contains mismatches。
- 当前主题配置中已设置
print: false,Theme Hope 的 TOC 打印按钮不会渲染。 - Theme Hope 的打印按钮本身只是调用
globalThis.print(),不引入额外大依赖。 - 对比构建显示,关闭打印按钮对 gzip 后 JS 体积影响只有几十到数百字节,属于噪声级别。
- 结论:打印按钮不是当前电脑端卡顿的主要原因。
- 注意:用户主动触发浏览器打印或打印预览时,超长页面仍可能因为分页、样式计算和大 DOM 导致短暂卡顿,但这只发生在打印流程中,不影响普通阅读首屏和滚动。
- 已禁用 Theme Hope 的
plugins.copyright。 - 原因:
@vuepress/plugin-copyright当前客户端代码在挂载时会执行document.querySelector("#app").style...,没有空判断;线上部署后出现过Cannot read properties of null (reading 'style'),会导致页面白屏。 - 影响:禁用后不再自动给复制内容追加“原文链接/版权信息”;页脚 Copyright 展示不受影响。
- 验证:clean build 通过,新的
app-*.js中已不再包含querySelector("#app")、userSelect、setupCopyright相关代码。
命令:
pnpm docs:build:clean
pnpm exec prettier --check docs/.vuepress/client.ts docs/.vuepress/components/DeferredLayoutToggle.vue PERFORMANCE_NOTES.md docs/.vuepress/components/LazyMermaid.vue docs/.vuepress/theme.ts docs/.vuepress/components/unlock/GlobalUnlock.vue结果:
- VuePress clean build 通过。
- Prettier 检查通过。
searchIndex.js未生成。photoswipe.esm不再出现在构建产物和客户端配置中。app-*.jsgzip 后约 70 KB。client-*.jsgzip 后约 129 KB。- 本地烟测确认
LayoutToggle会延后出现,不参与首屏同步渲染。
构建产物中仍然较大的页面 chunk 包括:
aqs.htmljava-concurrent-questions-03.htmljava8-common-new-features.htmlrocketmq-questions.htmlshell-intro.html
这些主要是长文、代码块、表格和图表内容本身带来的页面 chunk 成本。
- 处理老机器上的长文阅读卡顿:
- 结论:CDN 主要解决下载慢,Intel Mac 等老机器卡顿更可能来自长文页面的 HTML 解析、Vue hydration、DOM 渲染、代码块和图表执行成本。
- 目标:降低
aqs.html、java-concurrent-questions-03.html、java8-common-new-features.html、rocketmq-questions.html、shell-intro.html等重页面在老电脑上的主线程压力。 - 约束:长文拆分属于内容结构调整,执行前需要单独讨论。
- 讨论超长文章是否拆页:
- 保留原 URL 还是做跳转。
- 是否按问题组、章节或主题拆分。
- 对 SEO、外链、阅读路径的影响。
- 讨论 Mermaid 是否进一步按文章治理:
- 单页 Mermaid 数量过多的文章是否改为图片或拆分。
- 高频访问文章是否做单独优化。
- 评估图片预览能力是否恢复为按点击懒加载。
- 评估是否对代码块非常多的页面做折叠、分页或局部渲染。
- 不要长期缓存 HTML,否则发布后可能出现旧 HTML 引用不存在或不匹配的新 JS/CSS。
- hash 资源可以长期缓存,前提是构建产物文件名带内容 hash。
- 长文拆分属于内容结构调整,执行前需要单独讨论。