𝗛𝗼𝘄 𝗪𝗲 𝗖𝘂𝘁 𝗦𝗹𝗼𝘄 𝗥𝗲𝘀𝗽𝗼𝗻𝘀𝗲𝘀 𝗯𝘆 𝟴𝟬%
Subito is a top marketplace in Italy. We handle thousands of requests every minute. Our ad detail page is vital for SEO and revenue.
We moved this page from Next.js Pages Router to App Router. We did not stop product work to do this. We moved in small steps.
Here is how we did it.
𝗧𝗵𝗲 𝗔𝗽𝗽𝗿𝗼𝗮𝗰𝗵
We used an incremental strategy. We added the App Router tree alongside the old Pages Router. This let us ship new features while we migrated.
We used Server Components to own data fetching. This removed the need to pass data through many layers. We also created a Data Access Layer. This made sure we only fetch data once per request.
We used Suspense and the use() hook to enable HTML streaming. This shows a skeleton screen while data loads. The user sees the page faster.
𝗧𝗵𝗲 𝗖𝗵𝗮𝗹𝗹𝗲𝗻𝗴𝗲𝘀
Problem 1: HTTP 410 Gone Search engines need a 410 status when a page is permanently removed. The App Router does not have a built-in way to send this. We solved this using our Express server. The server intercepts the response and changes the status code.
Problem 2: HTML Streaming We found that our loading skeletons did not appear. The page stayed blank for seconds.
We discovered that Nginx and Akamai were buffering our responses. If the CDN buffers the HTML, streaming fails. We had to turn off buffering in Nginx and apply custom settings in Akamai. Once we fixed this, content streamed to the user perfectly.
𝗧𝗵𝗲 𝗥𝗼𝗹𝗹𝗼𝘂𝘁
We rolled out in two phases to protect our SEO.
Phase 1: We moved traffic to the App Router but kept streaming off. We did this one category at a time.
Phase 2: We enabled HTML streaming. We tested this on small categories first. Our SEO team monitored rankings for two weeks before we moved to the next category.
𝗧𝗵𝗲 𝗥𝗲𝘀𝘂𝗹𝘁𝘀
The results were huge. Before the migration, up to 40% of our responses were slow. After the migration, slow responses dropped by 80%.
Most pages now load in under 500ms.
𝗧𝗮𝗸𝗲𝗮𝘄𝗮𝘆𝘀
- Use parallel routing to migrate without stopping feature work.
- A custom server layer helps you control traffic and fix framework gaps.
- Streaming requires configuration at every layer. Check Nginx and your CDN.
- Test your SEO metrics during the rollout.
我们如何通过迁移到 Next.js App Router 将慢响应减少了 80%
在我们构建高性能 Web 应用的过程中,性能始终是我们关注的核心。最近,我们将核心产品从 Next.js 的 Pages Router 迁移到了 App Router。这次迁移不仅是一次架构升级,更是一次性能飞跃——我们将慢响应(slow responses)减少了 80%。
问题所在:Pages Router 的瓶颈
在 Pages Router 中,我们面临的主要挑战是客户端 JavaScript 包(bundle)的大小。由于几乎所有的组件都是客户端组件,我们需要将大量的逻辑、库和状态管理代码发送到用户的浏览器中。
这导致了几个关键问题:
- 巨大的 JS Bundle:用户必须下载大量代码才能看到页面。
- 水合(Hydration)延迟:浏览器需要解析并执行这些 JS,导致页面虽然看起来加载好了,但无法进行交互(导致 FID 和 TBT 指标升高)。
- 数据获取的“瀑布流”问题:虽然
getServerSideProps很有用,但在复杂的嵌套组件中,数据获取往往会导致请求串行,从而增加了整体等待时间。
解决方案:拥抱 App Router
App Router 的引入带来了 React Server Components (RSC) 的概念,这彻底改变了我们的开发模式。
1. React Server Components (RSC)
通过使用 RSC,我们可以将大部分逻辑留在服务器端。这意味着:
- 更小的客户端包:只有那些需要交互的组件(使用
'use client'指令)才会向客户端发送 JavaScript。 - 更快的首屏加载:服务器直接发送渲染好的 HTML,极大地减轻了客户端的工作负担。
2. 改进的数据获取与缓存
在 App Router 中,数据获取变得更加直观。我们可以直接在异步 Server Components 中使用 await fetch()。
// 在 Server Component 中直接获取数据
async function Page() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return <main>{/* 渲染数据 */}</main>;
}
Next.js 对 fetch 进行了扩展,提供了内置的缓存机制。这使得我们可以非常轻松地实现数据缓存和重新验证(revalidation),而无需引入复杂的外部状态管理库。
3. 流式传输(Streaming)与 Suspense
以前,如果一个页面有多个数据请求,我们必须等待所有请求完成后才能向用户发送页面。现在,我们可以利用 Streaming 和 Suspense。
通过在 loading.js 或使用 <Suspense> 边界,我们可以先向用户展示页面的骨架(skeleton),然后随着数据加载完成,逐步“流式”地填充内容。这极大地提升了感知性能(Perceived Performance)。
迁移过程中的挑战
迁移并不是一蹴而就的,我们也遇到了一些挑战:
- 思维模式的转变:从“客户端优先”转向“服务器优先”需要重新思考组件的职责划分。
- 第三方库的兼容性:一些依赖于浏览器 API 的旧库需要被包装在 Client Components 中才能正常工作。
- 缓存策略的复杂性:理解 Next.js 的不同缓存层级(Request Memoization, Data Cache, Full Route Cache, Router Cache)需要一定的学习成本。
结论
通过迁移到 App Router,我们不仅显著减少了慢响应,还大幅提升了开发体验。我们的 Core Web Vitals 指标得到了显著改善,用户体验也变得更加流畅。
如果你还在犹豫是否要进行迁移,我们的建议是:尽早开始,从小的组件开始,逐步过渡。