Skip to content

perf(blog): 服务端化文章渲染并优化首屏加载 - #3

Merged
ZiYan416 merged 1 commit into
masterfrom
codex/performance-page-load
Aug 3, 2026
Merged

perf(blog): 服务端化文章渲染并优化首屏加载#3
ZiYan416 merged 1 commit into
masterfrom
codex/performance-page-load

Conversation

@ZiYan416

@ZiYan416 ZiYan416 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

背景

公开页面曾将 Markdown 解析、高亮和完整拼音字典带入客户端,同时全局动画、弹窗和首页视频也增加了首屏资源成本。问题通过生产构建分块审计和真实页面加载回归发现,文章详情客户端引用一度达到 8,533,843 B raw,影响下载、解析和交互就绪时间。

根因是服务端可完成的内容渲染跨越了 Client Component 边界,通用 Markdown 工具又静态引入 pinyin,导致所有调用日期和阅读时长工具的客户端页面携带约 6.39 MB raw 的词典分块。

解决方案

将公开 Markdown 渲染和 slug 生成收口服务端,只保留复制、代码折叠等必要交互岛;同时拆分非首屏组件、延迟视频预载并用 CSS 动画替代全局 Framer Motion 依赖。选择继续使用现有拼音算法但限制为 server-only,以保持既有 URL 兼容。

主要变更

  • 文章渲染:服务端输出 Markdown、GFM 表格、HTML 清理和代码高亮,评论区通过 Suspense 流式加载。
  • 客户端边界:复制控制器与代码块交互保持客户端化,设置弹窗和文章预览改为按需加载并支持入口预取。
  • Slug 与上传:文章、标签及图片目录 slug 由服务端生成,文章详情直接复用数据库保存的标签 slug。
  • 首屏资源:使用 CSS 页面转场和描边动画,分阶段预载首页视频,并为图片和视频补充可重验证缓存头。
  • 文档与测试:新增性能基线、架构约束、服务端边界、XSS、slug 和标签映射回归测试。

行为变化

  • 新增行为:文章初始 HTML 直接包含可阅读正文和高亮代码;评论可在正文之后流式完成。
  • 修复行为:公开及后台客户端不再加载完整拼音字典,文章标签链接使用持久化 slug。
  • 保持不变:Markdown 显示效果、代码复制与折叠、明暗主题、图片上传目录规则、既有文章和标签 URL。
  • 未移除或弃用公开接口,不存在 Breaking Change。

验证

  • npm run lint:通过。
  • npm run typecheck:通过。
  • npm test:20 个测试文件、72 项测试全部通过。
  • npm run build:Next.js 16.2.12 生产构建通过,24 个静态页面生成成功。
  • git diff --check:通过。
  • 手工验证:生产模式覆盖首页、文章列表、搜索、标签、文章详情和未登录后台入口;桌面/移动端明暗主题无页面级横向溢出,控制台无错误。
  • 分块验证:文章详情客户端引用由 8,533,843 B 降至 574,479 B raw,客户端产物中不存在 pinyin 实现。
  • 未本地运行 npm run test:e2e 和 Supabase 数据库测试;本次无数据库变更,PR CI 将运行对应检查后再合并。

风险评估

  • 主要风险是 Server/Client 边界变化导致交互序列化或代码复制回归,已通过服务端静态渲染测试和浏览器交互回归缓解。
  • Slug 输出继续使用同版本 pinyin 算法,既有数据与 URL 不迁移;显式上传 slug 的优先级保持不变。
  • 性能影响为减少客户端下载和解析;服务端增加 Markdown 渲染工作,但沿用现有缓存与动态路由策略。
  • 不涉及凭据、安全策略、数据库结构或权限变更。

发布与迁移

  • 无需数据库迁移、配置变更、数据回填、重新索引或手工清缓存。
  • 需要正常重新构建并部署 Next.js 应用;静态资源哈希会自然刷新客户端缓存。
  • 支持直接回滚本 PR 的合并提交,无额外迁移步骤。

产物与证据

  • 性能基线与回归门禁:docs/performance.md
  • 提交:7075227 perf(blog): 服务端化文章渲染并优化首屏加载
  • 文章详情客户端引用:8,533,843 B → 574,479 B raw(-93.3%)。
  • 首页 JavaScript:353,499 B → 315,067 B gzip(-10.9%)。

后续工作

  • 合并部署后持续观察真实用户 Web Vitals;若依赖或路由增长导致分块回升,按性能文档中的构建门禁复核。

Reviewer 指引

  • 重点检查 Markdown 服务端边界、src/server/slug.tsserver-only 约束和上传 slug 回退优先级。
  • 重点验证文章标签跳转、代码复制/折叠以及后台文章保存和图片上传参数传递。
  • 确认构建产物不再包含拼音客户端分块,且文档性能数字与本次构建证据一致。

变更类型

  • 代码重构
  • 文档更新
  • 性能优化
  • Breaking Change

核对清单

  • 代码遵循项目规范并完成自审
  • 相关文档已更新
  • 本地 Lint、类型、单元测试与生产构建通过
  • 未引入新的运行时警告

- 文章渲染:将 Markdown、高亮和 slug 生成收口服务端,以交互岛保留复制、折叠及上传兼容行为。
- 首屏资源:按需加载设置与预览,延后视频预载,以 CSS 动画和缓存策略减少初始脚本与资源开销。
- 数据与文档:复用持久化标签 slug,流式加载评论,并记录性能基线与回归门禁。
- 验证:npm run lint(通过);npm run typecheck(通过);npm test(20 文件、72 测试通过);npm run build(通过);git diff --check(通过)。
- 行为边界:未修改数据库结构、既有 URL、Markdown 显示效果、图片存储协议或发布状态。
Copilot AI review requested due to automatic review settings August 3, 2026 06:04
@ZiYan416
ZiYan416 merged commit ada8f6b into master Aug 3, 2026
4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces blog first-load cost by moving Markdown rendering + slug generation behind a server-only boundary, trimming client bundles (notably avoiding shipping the pinyin dictionary), and deferring non-critical UI (modals/video preload/animations) to improve TTI while keeping existing URLs stable.

Changes:

  • Introduces server-only slug helpers and updates APIs/actions to generate slugs on the server; clients submit explicit slugs or empty values.
  • Refactors Markdown rendering to be server-rendered (sanitized + highlighted HTML), retaining only small client “islands” for copy/collapse interactions and streaming comments with Suspense.
  • Replaces/defers several client-side features (Framer Motion animations, settings/preview modals, background video preload) and adds caching headers + performance documentation/tests.

Reviewed changes

Copilot reviewed 36 out of 36 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/slug.test.ts Adds regression tests for server-only slug behavior and ensures pinyin stays server-side.
tests/post-repository.test.ts Updates repository test fixture to include tagLinks.
tests/post-model.test.ts Adds tests for persisted tag link mapping (name + slug).
tests/markdown.test.ts Removes slug generation expectations from markdown helpers.
tests/markdown-server-renderer.test.tsx Verifies server-rendered Markdown includes readable HTML, highlighting, and sanitization.
tests/markdown-renderer.test.tsx Adds copy-to-clipboard regression test for code blocks.
tests/code-block-shell.test.ts Updates tests to use extracted code language helpers module.
src/server/slug.ts Introduces server-only slug generation and upload-slug resolution helpers.
src/server/repositories/posts.ts Returns visible posts mapped with persisted tag links.
src/lib/markdown.ts Removes pinyin slug helper from shared markdown utilities.
src/features/tags/actions.ts Moves tag slug generation to server-only slug helper.
src/features/posts/model.ts Adds tagLinks mapping helpers and combined mapping function.
src/features/posts/image-upload.ts Adds articleTitle option and sends it to upload API.
src/features/posts/hooks/use-post-cover-upload.ts Extends hook API to include articleTitle for server-side slug fallback.
src/features/posts/editor/source-markdown-editor.tsx Passes articleTitle through editor image upload flow.
src/features/posts/editor/rich-editor.tsx Passes articleTitle through rich editor image upload flow.
src/features/posts/editor/editor.tsx Threads articleTitle through editor and upload actions.
src/features/posts/components/post-form.tsx Removes client slug generation; lazily loads preview modal with preload-on-hover/focus.
src/features/posts/components/post-card.tsx Replaces Framer Motion SVG border animation with CSS-based animation.
src/features/posts/components/markdown-renderer.tsx Converts Markdown renderer to server-safe output + client copy controller + lighter code copy path.
src/features/posts/components/markdown-copy-controller.tsx Introduces client-side selection copy handler without making whole renderer client-only.
src/features/posts/components/code-block-shell.tsx Removes highlight.js auto-detection; adds copyText-based copy path.
src/features/posts/code-block-language.ts Extracts code language parsing/detection logic into a lightweight module.
src/features/home/components/cinematic-hero.tsx Defers background video preloading until idle after initial buffering.
src/components/layout/site-loader.tsx Replaces loader animation with CSS transitions (removes Framer Motion dependency).
src/components/layout/navbar.tsx Lazily loads settings modal with preload-on-hover/focus and conditional mounting.
src/app/template.tsx Replaces page transition Framer Motion wrapper with CSS animation class.
src/app/post/[slug]/page.tsx Uses persisted tag slugs; streams comments via Suspense instead of blocking render.
src/app/globals.css Adds CSS animations for page enter + featured border, with reduced-motion support.
src/app/api/upload-image/route.ts Resolves upload folder slug on server using explicit slug or title fallback.
src/app/api/posts/create/route.ts Uses server-only slug helper for create route.
src/app/api/posts/[slug]/update/route.ts Uses server-only slug helper for update route.
README.md Updates performance architecture statement and links to new performance baseline doc.
next.config.ts Adds cache headers for /videos and /images static assets.
docs/performance.md Adds performance baseline, architectural constraints, and regression checklist.
docs/architecture.md Links architecture doc to performance/boundary constraints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 101 to 111
const handleCopy = async () => {
if (!onCopy) return
await onCopy()
if (onCopy) {
await onCopy()
} else if (copyText !== undefined) {
await navigator.clipboard.writeText(copyText)
} else {
return
}
setCopied(true)
window.setTimeout(() => setCopied(false), 2000)
}
Comment thread next.config.ts
Comment on lines 16 to 33
@@ -22,6 +31,15 @@ const nextConfig: NextConfig = {
},
],
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants