Skip to content

Commit 27f2eec

Browse files
author
zhaoshiwei1
committed
feat: add detailed documentation on development issues encountered in Spring Lament Blog, including SSR hydration errors, large file imports, and Mermaid chart rendering optimizations
1 parent 39ea94b commit 27f2eec

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed

docs/开发问题记录.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Spring Lament Blog 开发问题记录
2+
3+
## 1. 文章详情页水合不匹配
4+
5+
### 问题描述
6+
7+
Next.js SSR 水合错误:服务端渲染的 HTML 与客户端渲染不匹配,导致控制台出现 hydration 错误。
8+
9+
### 错误信息
10+
11+
```
12+
Hydration failed because the server rendered HTML didn't match the client
13+
Error: Text content does not match server-rendered HTML.
14+
```
15+
16+
### 问题原因
17+
18+
- 标题 ID 生成使用了 `Date.now()``Math.random()`
19+
- 服务端和客户端生成的随机值不同
20+
- 导致相同内容生成不同的 DOM 结构
21+
22+
### 解决方案
23+
24+
1. **实现稳定的哈希算法**
25+
26+
```javascript
27+
const generateStableUniqueId = (text: string, index: number) => {
28+
const baseId = text.toLowerCase().replace(/[^\w\u4e00-\u9fa5\s-]/g, "").replace(/\s+/g, "-");
29+
const hash = text.split('').reduce((a, b) => {
30+
a = ((a << 5) - a) + b.charCodeAt(0);
31+
return a & a;
32+
}, 0);
33+
return `${baseId}-${Math.abs(hash).toString(36)}-${index}`;
34+
};
35+
```
36+
37+
2. **预生成所有标题 ID**:在 useMemo 中一次性解析所有标题并生成稳定的 ID 映射
38+
39+
3. **统一 Mermaid ID 生成**:使用内容哈希而非随机数
40+
41+
### 验证结果
42+
43+
- ✅ SSR 水合错误完全消除
44+
- ✅ 服务端客户端 ID 生成一致
45+
- ✅ 目录跳转功能正常
46+
47+
---
48+
49+
## 2. 导入导出文件太大
50+
51+
### 问题描述
52+
53+
应用打包后文件体积过大,影响加载性能。
54+
55+
### 待分析
56+
57+
- Bundle 分析
58+
- 代码分割优化
59+
- 依赖项瘦身
60+
61+
---
62+
63+
## 3. Mermaid重绘导致的页面闪烁
64+
65+
### 问题描述
66+
67+
在博客文章页面滚动时,Mermaid 图表出现明显的重渲染闪烁现象,严重影响用户体验。
68+
69+
### 复现步骤
70+
71+
1. 访问包含 Mermaid 图表的文章页面
72+
2. 向下滚动页面
73+
3. 观察到 Mermaid 图表频繁闪烁/重新渲染
74+
75+
### 问题根因分析
76+
77+
1. **组件频繁重渲染**:滚动时 `activeHeading` 状态频繁更新 → 整个 MarkdownRenderer 组件重新渲染
78+
2. **Mermaid 重新初始化**:每次组件重渲染时,Mermaid 图表都会重新渲染
79+
3. **资源浪费**:主题检测的 MutationObserver 被重复创建
80+
81+
### 解决方案
82+
83+
#### 3.1 Mermaid组件优化 (`src/components/markdown/mermaid.tsx`)
84+
85+
**添加全局缓存机制**
86+
87+
```typescript
88+
// 全局缓存已渲染的图表
89+
const mermaidCache = new Map<string, { svg: string; theme: string }>();
90+
```
91+
92+
**优化主题检测**
93+
94+
```typescript
95+
// 只在组件挂载时创建一次 MutationObserver
96+
useEffect(() => {
97+
setIsDark(currentTheme === "dark");
98+
99+
const checkTheme = () => {
100+
const newIsDark = document.documentElement.classList.contains("dark");
101+
setIsDark(newIsDark);
102+
};
103+
104+
const observer = new MutationObserver(checkTheme);
105+
observer.observe(document.documentElement, {
106+
attributes: true,
107+
attributeFilter: ["class"],
108+
});
109+
110+
return () => observer.disconnect();
111+
}, []); // 只在挂载时执行一次
112+
```
113+
114+
**渲染防抖机制**
115+
116+
```typescript
117+
const renderAttempted = useRef(false);
118+
119+
const renderChart = async () => {
120+
if (!chart || renderAttempted.current) return;
121+
122+
const theme = isDark ? "dark" : "default";
123+
const cacheKey = `${chart}-${theme}`;
124+
125+
// 检查缓存
126+
const cached = mermaidCache.get(cacheKey);
127+
if (cached) {
128+
setSvg(cached.svg);
129+
setError("");
130+
return;
131+
}
132+
133+
renderAttempted.current = true;
134+
// ... 渲染逻辑
135+
// 存入缓存
136+
mermaidCache.set(cacheKey, { svg: renderedSvg, theme });
137+
};
138+
```
139+
140+
#### 3.2 MarkdownRenderer组件优化 (`src/components/markdown/markdown-renderer.tsx`)
141+
142+
**使用 useMemo 防止不必要的重渲染**
143+
144+
```typescript
145+
{useMemo(
146+
() => (
147+
<ReactMarkdown
148+
remarkPlugins={[remarkGfm]}
149+
rehypePlugins={[rehypeRaw]}
150+
components={{...}}
151+
>
152+
{content || ""}
153+
</ReactMarkdown>
154+
),
155+
[content, headingIdMap] // 只依赖 content 和 headingIdMap
156+
)}
157+
```
158+
159+
### 性能提升效果
160+
161+
**优化前**:
162+
163+
- ❌ 滚动时 Mermaid 图表频繁闪烁
164+
- ❌ 每次滚动都重新渲染整个 Markdown 内容
165+
- ❌ 重复创建 MutationObserver 和事件监听器
166+
167+
**优化后**:
168+
169+
- ✅ 滚动时 Mermaid 图表保持稳定
170+
- ✅ 缓存机制大幅提升渲染性能
171+
- ✅ 资源复用,减少内存占用
172+
- ✅ 用户体验显著改善
173+
174+
### 关键经验总结
175+
176+
1. **识别并分离频繁变化的状态**:将滚动相关状态与内容渲染分离
177+
2. **为昂贵的渲染操作实现缓存**:避免重复的异步渲染
178+
3. **合理管理副作用的生命周期**:确保资源正确清理
179+
4. **使用 React 优化手段**:useMemo、useCallback 等
180+
181+
---
182+
183+
## 开发经验总结
184+
185+
### 性能优化原则
186+
187+
1. 优先识别性能瓶颈,避免过度优化
188+
2. 合理使用缓存机制
189+
3. 注意 React 渲染优化
190+
4. 监控内存使用情况
191+
192+
### 调试技巧
193+
194+
1. 使用 React DevTools 分析组件渲染
195+
2. 利用浏览器性能面板定位问题
196+
3. 添加适当的日志和断点
197+
4. 重视用户反馈和实际体验

0 commit comments

Comments
 (0)