𝗛𝗼𝘄 𝗪𝗲 𝗖𝘂𝘁 𝗦𝗹𝗼𝘄 𝗥𝗲𝘀𝗽𝗼𝗻𝘀𝗲𝘀 𝗯𝘆 𝟴𝟬%
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.
Cách chúng tôi giảm 80% các phản hồi chậm khi chuyển sang Next.js App Router
Trong bài viết này, chúng tôi sẽ chia sẻ hành trình chuyển đổi từ Next.js Pages Router sang App Router và cách nó đã giúp chúng tôi cắt giảm 80% các phản hồi chậm.
Vấn đề: Tốc độ phản hồi chậm
Trước đây, ứng dụng của chúng tôi gặp phải tình trạng độ trễ (latency) cao, đặc biệt là khi người dùng điều hướng giữa các trang. Sau khi phân tích, chúng tôi nhận thấy nguyên nhân chính đến từ:
- Client-side Data Fetching & Waterfalls: Chúng tôi thực hiện lấy dữ liệu chủ yếu ở phía client. Điều này tạo ra hiệu ứng "waterfall" (thác nước), nơi các yêu cầu API phải chờ đợi nhau (ví dụ: component cha phải fetch xong dữ liệu thì component con mới bắt đầu fetch), làm kéo dài thời gian hiển thị nội dung.
- Kích thước Bundle lớn: Do sử dụng quá nhiều Client Components, lượng JavaScript gửi xuống trình duyệt là rất lớn, làm chậm quá trình hydration.
- Thiếu cơ chế Streaming: Người dùng phải chờ toàn bộ dữ liệu được tải xong mới thấy được giao diện, thay vì thấy được các phần của trang ngay lập tức.
Giải pháp: Chuyển sang Next.js App Router
Việc chuyển sang App Router đã thay đổi hoàn toàn cách chúng tôi xử lý dữ liệu và render giao diện.
1. Tận dụng React Server Components (RSC)
Với App Router, chúng tôi đã chuyển phần lớn logic lấy dữ liệu sang Server Components. Điều này mang lại hai lợi ích lớn:
- Giảm kích thước bundle: Logic lấy dữ liệu và các thư viện phụ thuộc chỉ chạy trên server, không cần gửi xuống client. Điều này giúp giảm đáng kể lượng JavaScript mà trình duyệt phải tải và thực thi.
- Loại bỏ Waterfall: Chúng tôi có thể thực hiện các truy vấn dữ liệu song song ngay trên server trước khi gửi HTML về trình duyệt, giúp giảm thiểu thời gian chờ đợi của người dùng.
// Ví dụ về việc lấy dữ liệu trực tiếp trong Server Component
async function Page() {
const data = await fetchData(); // Fetching trực tiếp trên server
return (
<div>
<h1>{data.title}</h1>
{/* Render dữ liệu */}
</div>
);
}
2. Streaming với Suspense
Thay vì bắt người dùng chờ đợi toàn bộ trang web, chúng tôi sử dụng Suspense để thực hiện streaming. Điều này cho phép:
- Hiển thị các thành phần giao diện tĩnh (như layout, header, sidebar) ngay lập tức.
- "Stream" các thành phần động (như danh sách sản phẩm, thông tin người dùng) ngay khi dữ liệu sẵn sàng.
Điều này cải thiện đáng kể các chỉ số như First Contentful Paint (FCP) và Time to First Byte (TTFB).
import { Suspense } from 'react';
import { SlowComponent, Skeleton } from './components';
export default function Page() {
return (
<section>
<h1>Dashboard</h1>
<Suspense fallback={<Skeleton />}>
<SlowComponent />
</Suspense>
</section>
);
}
3. Cấu trúc Layouts và Caching
App Router cung cấp cơ chế Layouts lồng nhau (nested layouts) mạnh mẽ hơn. Điều này giúp:
- Giữ nguyên trạng thái: Các thành phần không thay đổi (như thanh điều hướng) sẽ không bị re-render khi người dùng điều hướng giữa các trang con.
- Tối ưu hóa Caching: Tận dụng cơ chế caching mặc định của Next.js để giảm thiểu số lượng yêu cầu không cần thiết đến API.
Kết quả
Sau khi hoàn tất quá trình di chuyển, chúng tôi đã ghi nhận những con số ấn tượng:
- Giảm 80% các phản hồi chậm (slow responses).
- Cải thiện đáng kể các chỉ số Core Web Vitals.
- Trải nghiệm người dùng mượt mà hơn nhờ việc giảm tải JavaScript ở phía client.
Kết luận
Việc chuyển đổi sang Next.js App Router không chỉ là một sự thay đổi về cấu trúc thư mục, mà là một sự thay đổi về tư duy kiến trúc: ưu tiên xử lý ở server và tối ưu hóa việc gửi dữ liệu đến client. Nếu ứng dụng của bạn đang gặp vấn đề về hiệu suất do client-side fetching, việc chuyển sang App Router có thể là một bước đi mang tính quyết định.