跳到主要内容
稻草人
プロフィール

EP.02

💻Technology

维护Next.js 14 + TypeScript博客时,我会这样做性能优化

Next.js 14 と TypeScript でブログを速く、検索されやすく、保守しやすくするための実践メモ。

BY 稻, 草人

先说明一下:这篇文章针对仍然运行在Next.js 14 App Router上的既有博客项目,不代表Next.js 14是当前最新版本。

维护旧版本项目时,我不会一看到性能问题就立刻升级大版本。升级和优化是两个任务。先测量瓶颈、做低风险优化,再单独评估升级,原因会更容易判断。

目次

先测量,不凭感觉

我会先确认:

  • 首页和文章页的LCP、CLS、INP
  • 首次加载的JavaScript大小
  • 最大图片和字体请求
  • 服务端响应时间
  • 缓存命中情况
  • 移动网络下的实际表现

Lighthouse适合发现问题,但还要结合Chrome DevTools的Network、Performance以及真实用户数据。一次本地高分不能代表所有用户。

尽量保留Server Component

App Router下组件默认可以在服务端执行。只有需要浏览器状态、事件和浏览器API时才添加'use client'。

// Server Component
export default async function ArticlePage({ params }: Props) {
  const article = await getArticle(params.slug)
  return <Article article={article} />
}

如果在页面最上层加入'use client',下面大量组件可能进入客户端边界,JavaScript体积也会增加。

更好的方式是把真正交互的部分缩小:

<ArticleBody article={article} />
<LikeButton articleId={article.id} />

正文保持Server Component,点赞按钮单独成为Client Component。

图片通常是第一优化对象

博客的首图很容易成为LCP元素。使用next/image时要设置正确尺寸和sizes。

<Image
  src={article.coverUrl}
  alt={article.coverAlt}
  width={1200}
  height={675}
  sizes="(max-width: 768px) 100vw, 800px"
  priority
/>

priority只给首屏真正重要的图片,不能每张都加。图片尺寸要接近实际显示尺寸,不能让手机也下载超大原图。

还要确认:

  • WordPress等远程图片域名配置
  • alt是否准确
  • 封面尺寸比例是否统一
  • 正文图片是否懒加载
  • 是否存在布局抖动

字体与CLS

使用next/font可以减少外部字体请求并改善加载方式,但字体种类和字重仍然要控制。

const notoSansJP = Noto_Sans_JP({
  subsets: ['latin'],
  weight: ['400', '700'],
  display: 'swap'
})

如果中文、日文、英文混合,字体文件可能很大。不要为了一个标题导入过多字重。还要准备合理的fallback字体,减少切换时的版面变化。

数据获取与缓存策略

博客首页、分类页、文章页的更新频率不同,不应该全部使用相同缓存。

思路可以是:

  • 基本不变的介绍页:长缓存
  • 文章详情:发布后缓存,更新时再验证
  • 首页最新文章:较短再验证时间
  • 登录状态或预览:不使用公开缓存

缓存不是越久越好。最重要的是编辑者更新文章后,页面什么时候变化,以及失败时如何发现。

如果数据来自WordPress,可以在发布时通过Webhook触发Next.js的再验证。Webhook必须验证密钥,并记录失败日志。

减少不必要的客户端依赖

博客常见的体积来源包括:

  • 整套UI库只使用一个组件
  • 大型日期库只用于格式化日期
  • 所有页面加载代码高亮
  • 所有文章加载轮播或图表库

使用Bundle分析工具确认实际内容,不要只根据package.json猜测。

只在需要时动态加载重组件:

const Chart = dynamic(() => import('./Chart'), {
  loading: () => <p>Loading...</p>
})

但普通文本组件没有必要全部dynamic。拆分本身也有网络和维护成本。

Markdown与代码高亮

技术博客经常在构建时处理Markdown和代码高亮。如果把高亮库整个送到客户端,文章页会变重。

可以优先考虑:

  • 构建时或服务端高亮
  • 只加载实际使用的语言
  • 普通文章不加载代码功能
  • 对生成后的HTML进行安全处理

TypeScript如何帮助优化

TypeScript不会直接让页面更快,但能让数据边界更清楚。

type ArticleCard = {
  slug: string
  title: string
  excerpt: string
  coverUrl: string
}

首页卡片不需要正文,就不要把完整文章对象传给客户端组件。明确的小型View Model能减少误用,也让接口更容易优化。

SEO和性能一起检查

速度优化不能破坏SEO。文章页还要确认:

  • 服务端输出标题和正文
  • metadata、canonical和Open Graph
  • sitemap更新时间
  • 文章删除后的404或301
  • 分类与分页链接
  • JSON-LD内容正确

如果为了减少HTML而把正文改成客户端请求,搜索引擎和分享预览可能受到影响。

第三方脚本

GA、广告、聊天、热图等脚本可能比自己的代码更重。每个脚本都要回答:

  • 是否真的需要
  • 哪些页面需要
  • 能否延后加载
  • 用户同意前是否应该加载
  • 加载失败会不会影响主功能

不要只优化几十KB的业务代码,却忽略多个第三方脚本。

升级与优化分开

Next.js 14项目已经属于旧版本维护场景。完成性能基线后,应另外评估:

  • Node.js版本与部署环境
  • Next.js 14使用的具体小版本
  • 已知安全问题
  • React与第三方库兼容性
  • App Router行为变化
  • 测试与回滚方案

升级可能带来性能与安全收益,但最好单独开分支、阅读目标版本迁移文档并完整回归,而不是混在图片优化中一起上线。

检查清单

  • 找到了真实LCP元素
  • 'use client'边界足够小
  • 首图尺寸、sizes、优先级正确
  • 字体种类与字重受控
  • 页面缓存符合更新频率
  • 发布后的再验证流程可观察
  • Bundle中没有明显无用依赖
  • 代码高亮没有全部放到客户端
  • 第三方脚本按需加载
  • SEO、404、sitemap没有被破坏
  • 升级任务与性能优化分开

总结

博客优化通常不需要先做复杂技巧。最大的收益往往来自缩小Client Component范围、正确处理首图和字体、制定清晰缓存策略,以及删除不必要的第三方脚本。

对Next.js 14旧项目,我会先建立性能基线,完成低风险优化,再单独安排版本升级。这样出了问题更容易回滚,也更容易知道哪项修改真正有效。

参考资料