Bài 28.3: getStaticProps và getServerSideProps

Bài 28.3: getStaticProps và getServerSideProps
Chào mừng bạn đến với bài học hôm nay trong chuỗi bài viết về Lập trình Web Front-end! Chúng ta đang khám phá sức mạnh của Next.js, một framework React giúp việc xây dựng các ứng dụng web hiệu suất cao trở nên dễ dàng hơn. Một trong những điểm mạnh lớn nhất của Next.js là cách nó xử lý việc tải dữ liệu (data fetching). Thay vì chỉ dựa vào client-side rendering (CSR) như React thuần, Next.js cho phép chúng ta tải dữ liệu ở phía server hoặc thậm chí tại thời điểm build, mang lại những lợi ích đáng kể về hiệu suất, trải nghiệm người dùng và tối ưu hóa công cụ tìm kiếm (SEO).
Trong bài viết này, chúng ta sẽ tập trung vào hai hàm tải dữ liệu cốt lõi và phổ biến nhất trong Next.js (đối với App Router có cách khác, nhưng với Pages Router thì đây là những hàm chính): getStaticProps
và getServerSideProps
. Hiểu rõ khi nào và làm thế nào để sử dụng chúng là chìa khóa để xây dựng các ứng dụng Next.js hiệu quả.
getStaticProps: Tải dữ liệu tại thời điểm Build (Static Generation)
Hãy bắt đầu với getStaticProps
. Đây là hàm dùng để tải dữ liệu cho trang web của bạn tại thời điểm bạn build ứng dụng Next.js. Tức là, hàm này sẽ chỉ chạy một lần duy nhất trong quá trình build (khi bạn chạy next build
). Kết quả dữ liệu tải về sẽ được sử dụng để tạo ra một file HTML tĩnh cho trang đó.
Cách thức hoạt động:
- Bạn định nghĩa một hàm
async
có tên làgetStaticProps
trong file page component của bạn (ví dụ:pages/about.js
,pages/posts/[id].js
). - Khi bạn chạy
next build
, Next.js sẽ tìm và thực thi hàmgetStaticProps
này trên server. - Hàm này sẽ tải dữ liệu (ví dụ: từ API, database, hoặc file markdown).
- Dữ liệu được trả về từ
getStaticProps
thông qua objectprops
. - Next.js sau đó sử dụng dữ liệu này để pre-render (tạo sẵn) file HTML tĩnh cho trang tương ứng.
- File HTML tĩnh này (cùng với JavaScript cần thiết để React hoạt động) sẽ được phục vụ cho người dùng khi họ truy cập trang.
Khi nào sử dụng getStaticProps
?
Bạn nên sử dụng getStaticProps
khi:
- Dữ liệu cần tải không thay đổi thường xuyên (ví dụ: danh sách bài blog, trang giới thiệu tĩnh, danh sách sản phẩm cố định).
- Nội dung trang cần được SEO tốt (vì HTML tĩnh được tạo sẵn, các bot tìm kiếm có thể dễ dàng crawl).
- Bạn muốn tốc độ tải trang siêu nhanh (vì trang tĩnh có thể được phục vụ trực tiếp từ CDN mà không cần server render lại mỗi lần).
- Dữ liệu không phụ thuộc vào thông tin của request cụ thể (ví dụ: cookie, query params - trừ khi sử dụng với dynamic routes và
getStaticPaths
).
Lợi ích:
- Hiệu suất tối ưu: Trang được tạo tĩnh và có thể cache trên CDN, giảm tải cho server và tăng tốc độ tải cho người dùng trên toàn cầu.
- SEO vượt trội: Bot tìm kiếm dễ dàng đọc nội dung HTML tĩnh.
- Giảm chi phí server: Không cần server render trên mỗi request.
Ví dụ minh họa:
Giả sử bạn muốn hiển thị danh sách các bài viết blog. Dữ liệu bài viết thường không thay đổi theo mỗi giây hay mỗi phút, nên đây là trường hợp lý tưởng cho getStaticProps
.
// pages/blog.js
import React from 'react';
// Component nhận dữ liệu từ getStaticProps qua props
function Blog({ posts }) {
return (
<div>
<h1>**Danh sách bài viết Blog**</h1>
<ul>
{/* Render danh sách bài viết */}
{posts.map((post) => (
<li key={post.id}>
**{post.title}**
<p>{post.excerpt}</p>
</li>
))}
</ul>
</div>
);
}
// Hàm getStaticProps chạy tại thời điểm build
export async function getStaticProps() {
// Simulates fetching data from an API or database
// This runs ONLY on the server and at build time
const res = await fetch('https://api.example.com/posts'); // Thay thế bằng API thực tế của bạn
const posts = await res.json();
// Trả về dữ liệu qua props để truyền vào component Blog
return {
props: {
posts, // Will be passed to the page component as props
},
// Optional: Incremental Static Regeneration (ISR)
// Revalidate the page every 60 seconds
// if a request comes in
// revalidate: 60, // In seconds
};
}
export default Blog;
Giải thích code:
- Chúng ta định nghĩa một hàm
async function getStaticProps()
ở cuối file page componentblog.js
. - Bên trong hàm, chúng ta sử dụng
fetch
(hoặc bất kỳ thư viện tải dữ liệu nào khác) để gọi tới API lấy danh sách bài viết. Quan trọng: Lệnhfetch
này được thực thi trên server tại thời điểm build. - Kết quả nhận được từ API được parse thành JSON.
- Hàm
getStaticProps
trả về một object. Object này phải có một key làprops
. Giá trị củaprops
là một object khác chứa dữ liệu bạn muốn truyền cho component trang. Ở đây, chúng ta truyền object{ posts }
. - Component
Blog
nhận object{ posts }
này thông qua tham sốprops
. - Khi bạn chạy
next build
, Next.js sẽ thực thigetStaticProps
, tải dữ liệu bài viết, và sử dụng dữ liệu đó để tạo fileblog.html
tĩnh. - Lưu ý: Dòng
revalidate: 60
là một tính năng tùy chọn gọi là Incremental Static Regeneration (ISR). Nó cho phép Next.js cập nhật trang tĩnh này sau khi deploy mà không cần build lại toàn bộ ứng dụng. Nếu có yêu cầu truy cập trang này sau 60 giây kể từ lần cuối nó được tạo/cập nhật, Next.js sẽ chạy lạigetStaticProps
ở background để tạo bản mới.
getServerSideProps: Tải dữ liệu theo mỗi Yêu cầu (Server-Side Rendering)
Tiếp theo là getServerSideProps
. Hàm này được sử dụng khi bạn cần tải dữ liệu cho trang theo mỗi yêu cầu (on every request) của người dùng tới server.
Cách thức hoạt động:
- Bạn định nghĩa một hàm
async
có tên làgetServerSideProps
trong file page component của bạn. - Khi người dùng gửi một yêu cầu (request) tới trang này, Next.js server sẽ tìm và thực thi hàm
getServerSideProps
cho yêu cầu cụ thể đó. - Hàm này sẽ tải dữ liệu. Dữ liệu này có thể phụ thuộc vào thông tin của request (ví dụ: query params, headers, cookies).
- Dữ liệu được trả về từ
getServerSideProps
thông qua objectprops
. - Next.js sau đó sử dụng dữ liệu này để render (tạo) file HTML trên server cho yêu cầu đó.
- File HTML được tạo ra sẽ được gửi về browser của người dùng.
Khi nào sử dụng getServerSideProps
?
Bạn nên sử dụng getServerSideProps
khi:
- Dữ liệu cần tải thay đổi thường xuyên hoặc cần phải cập nhật theo thời gian thực trên mỗi lần truy cập.
- Nội dung trang là cá nhân hóa dựa trên người dùng (ví dụ: dashboard người dùng, giỏ hàng).
- Dữ liệu cần tải phụ thuộc vào thông tin của request (ví dụ: đọc cookie, query parameters từ URL).
- Nội dung cần được SEO tốt, nhưng không thể tạo tĩnh được do tính động của dữ liệu.
Lợi ích:
- Dữ liệu luôn mới nhất: Mỗi yêu cầu đều được tải dữ liệu mới.
- SEO tốt: Giống như
getStaticProps
, HTML được tạo sẵn trên server, giúp bot tìm kiếm dễ dàng đọc nội dung (tốt hơn CSR). - Truy cập thông tin request: Có thể truy cập
req
vàres
objects.
Nhược điểm:
- Hiệu suất chậm hơn
getStaticProps
: Vì server phải xử lý data fetching và rendering cho mỗi yêu cầu, thời gian phản hồi sẽ lâu hơn so với việc phục vụ file tĩnh từ CDN. Điều này có thể ảnh hưởng đến Time to First Byte (TTFB).
Ví dụ minh họa:
Giả sử bạn muốn hiển thị thời gian hiện tại trên trang. Thời gian thay đổi theo từng giây, nên bạn cần dữ liệu mới nhất cho mỗi lần người dùng truy cập.
// pages/current-time.js
import React from 'react';
// Component nhận dữ liệu từ getServerSideProps qua props
function CurrentTime({ serverTime }) {
return (
<div>
<h1>**Thời gian hiện tại (Server-Side Rendering)**</h1>
<p>Thời gian được tải từ server tại thời điểm bạn truy cập:</p>
<p>**{serverTime}**</p>
{/* Bạn có thể thêm client-side JS để cập nhật thời gian sau khi trang tải */}
</div>
);
}
// Hàm getServerSideProps chạy trên server cho mỗi yêu cầu
export async function getServerSideProps(context) {
// 'context' object contains request-specific information like
// params, req, res, query, etc.
// const { req, res, query } = context;
// Simulate fetching dynamic data - here, just getting current time
// This runs on the server on EVERY request
const currentTime = new Date().toISOString();
// In a real app, you might fetch data based on user cookies (req.headers.cookie)
// or query params (query)
// Trả về dữ liệu qua props để truyền vào component CurrentTime
return {
props: {
serverTime: currentTime, // Will be passed to the page component as props
},
};
}
export default CurrentTime;
Giải thích code:
- Chúng ta định nghĩa một hàm
async function getServerSideProps(context)
trong file page componentcurrent-time.js
. Hàm này nhận một tham sốcontext
chứa thông tin chi tiết về yêu cầu đến (nhưreq
,res
,query
,params
). - Bên trong hàm, chúng ta lấy thời gian hiện tại bằng
new Date().toISOString()
. Quan trọng: Đoạn code này được thực thi trên server mỗi lần có yêu cầu đến trang/current-time
. - Hàm trả về một object phải có key là
props
, chứa dữ liệu (serverTime
) để truyền cho componentCurrentTime
. - Khi người dùng truy cập trang
/current-time
, Next.js server sẽ chạygetServerSideProps
, lấy thời gian hiện tại, tạo file HTML chứa thời gian đó, và gửi về browser. Mỗi lần refresh trang (hoặc truy cập lại), quá trình này lặp lại, đảm bảo thời gian hiển thị luôn là thời gian lúc yêu cầu được xử lý trên server.
getStaticProps vs getServerSideProps: Chọn lựa phù hợp
Việc lựa chọn giữa getStaticProps
và getServerSideProps
phụ thuộc vào bản chất của dữ liệu bạn cần tải và yêu cầu về hiệu suất/SEO của trang. Dưới đây là bảng so sánh nhanh để giúp bạn đưa ra quyết định:
Tiêu chí | getStaticProps |
getServerSideProps |
---|---|---|
Thời điểm chạy | Tại thời điểm build (chỉ 1 lần) | Trên server theo mỗi yêu cầu |
Loại dữ liệu | Tĩnh, ít thay đổi | Động, thay đổi thường xuyên, cá nhân hóa |
Phụ thuộc Request | Không phụ thuộc (hoặc dùng getStaticPaths ) |
Phụ thuộc vào thông tin request (req , query ) |
Hiệu suất | Rất nhanh (HTML tĩnh từ CDN) | Chậm hơn (phải đợi server render) |
Khả năng Cache | Rất tốt (bởi CDN) | Khó cache (nội dung động) |
SEO | Tuyệt vời (HTML có sẵn) | Tốt (HTML có sẵn trước Client JS) |
Use Cases | Trang giới thiệu, bài blog, docs, danh sách sản phẩm tĩnh | Dashboard người dùng, kết quả tìm kiếm, giỏ hàng, trang cá nhân hóa |
Lời khuyên:
- Luôn ưu tiên sử dụng
getStaticProps
nếu có thể. Static Generation mang lại hiệu suất tốt nhất và giảm tải cho server. Kết hợp với ISR (revalidate
), bạn vẫn có thể cập nhật nội dung tĩnh một cách hiệu quả. - Chỉ dùng
getServerSideProps
khi bạn thực sự cần dữ liệu cập nhật theo từng yêu cầu hoặc dữ liệu đó phụ thuộc vào request cụ thể. - Nếu dữ liệu không cần pre-render (ví dụ: hiển thị danh sách tùy chọn sau khi người dùng click nút, hoặc nội dung không cần SEO), bạn có thể tải dữ liệu ở phía client sau khi trang đã được render ban đầu (ví dụ: sử dụng
useEffect
kết hợp vớifetch
hoặc các thư viện như SWR/React Query). Nhưng bài viết này tập trung vào pre-rendering, nên chúng ta sẽ không đi sâu vào client-side fetching ở đây.
Hiểu và vận dụng linh hoạt getStaticProps
và getServerSideProps
sẽ giúp bạn xây dựng các ứng dụng Next.js vừa có hiệu suất cao, vừa tối ưu cho SEO và vẫn đáp ứng được các nhu cầu hiển thị dữ liệu động. Chúc bạn thực hành thành công!
Comments