Next.js 博客性能优化:告别 dangerouslySetInnerHTML,拥抱 React Markdown
在构建个人博客时,我们通常会面临一个选择:如何渲染 Markdown 内容?
最简单的方法是在服务端将 Markdown 转为 HTML字符串,然后使用 dangerouslySetInnerHTML 渲染。我的博客早期也是这么做的:
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
这种方法虽然简单,但有两个致命缺点:
- 安全性风险:如果不小心,容易导致 XSS 攻击(虽然使用了 sanitization 库可以缓解)。
- 性能短板:无法利用 React 组件的优势。最典型的就是图片渲染——原本的 Markdown 图片会被渲染为普通的
<img>标签,导致布局偏移 (CLS) 和加载性能问题。
为了解决这个问题,我决定重构博客的渲染层,引入 react-markdown 配合 next/image 进行深度优化。
1. 架构调整
首先,我们需要修改后端的数据读取逻辑 (lib/posts.ts)。之前我们是在服务端完全编译成 HTML,现在我们需要将原始 Markdown (Raw Content) 传递给前端。
// lib/posts.ts export async function getPostData(id: string) { // ... return { id, contentHtml, // 保留 HTML 以备不时之需 content: rawContent, // ✅ 新增:传递原始 Markdown // ... }; }
2. 前端渲染重构
接下来是重头戏。我们使用 react-markdown 替换 dangerouslySetInnerHTML,并通过 components 属性拦截 img 标签的渲染。
// app/posts/[id]/page.tsx import ReactMarkdown from 'react-markdown'; import Image from 'next/image'; // ... <ReactMarkdown components={{ // 拦截 img 标签 img: ({ node, ...props }) => { if (!props.src) return null; return ( <span className="block relative w-full aspect-video my-6 rounded-lg overflow-hidden bg-gray-100 dark:bg-gray-800"> <Image src={props.src} alt={props.alt || ''} fill // 使用 fill 布局,自动适应容器 className="object-contain" // 保持图片比例 sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" // 响应式加载 /> </span> ); }, // 我们还可以顺便拦截代码块,实现客户端高亮 code: ({ ...props }) => { /* ... */ } }} > {postData.content} </ReactMarkdown>
3. 为什么这样更好?
3.1 消除 CLS (Cumulative Layout Shift)
普通的 <img> 标签在图片加载完成前,高度通常是 0。当图片加载出来后,页面内容会突然被“撑开”,导致用户阅读体验极差。
使用 next/image 的 fill 模式配合 aspect-video 容器,我们可以预先为图片保留占位空间,图片加载过程中页面布局稳如泰山。
3.2 自动格式优化
Next.js 的 Image 组件会自动将图片转换为 WebP 或 AVIF 等现代格式,体积通常能减小 30%-50%,大幅提升页面加载速度。
3.3 懒加载 (Lazy Loading)
默认开启的懒加载机制,意味着只有当用户滚动到图片位置时才会请求网络,节省了带宽。
4. 遇到的坑
在迁移过程中,最麻烦的是 next/image 需要确定的宽/高或者父容器尺寸。由于 Markdown 图片通常不带尺寸信息,我选择了 fill 布局配合 CSS 的 aspect-ratio。这对于大多数标准比例的截图和照片效果很好,但对于超长图或竖图可能会有留白。
目前的解决方案是统一使用 object-contain 确保图片完整显示,底色使用浅灰背景填充,视觉上也比较和谐。
通过这次重构,博客的 Lighthouse 性能评分中的 CLS 指标降到了 0,图片加载体验也丝滑了许多。这就是 Modern Web Development 的魅力:用更好的工具,给用户更好的体验。