Hello mọi người lại là Slao đây !
Sự khác biệt giữa Client và Server Component và những điều bạn chưa biết
Server Component có thể
- Truy xuất dữ liệu trực tiếp: Server Components có thể lấy dữ liệu trực tiếp từ các nguồn dữ liệu mà không cần thông qua client. Điều này giúp tối ưu hóa hiệu suất vì dữ liệu được xử lý trực tiếp trên máy chủ trước khi gửi đến client.
- Truy cập tài nguyên backend (cơ sở dữ liệu, API nội bộ): Chúng có thể tương tác trực tiếp với các tài nguyên backend như cơ sở dữ liệu hoặc các API nội bộ, giúp đơn giản hóa quy trình phát triển và bảo mật hơn.
- Bảo mật thông tin nhạy cảm (token, API keys): Vì các thành phần này chạy trên máy chủ, các thông tin nhạy cảm như token hay API keys không bao giờ được gửi đến client, giảm thiểu rủi ro bị lộ thông tin.
- Chứa các dependencies lớn không cần gửi đến client: Server Components có thể chứa các thư viện hoặc dependencies lớn mà không cần gửi chúng đến client, giúp giảm tải cho trình duyệt và cải thiện hiệu suất tổng thể.
Client Component có thể
- Thêm tính tương tác với các sự kiện (onClick, onChange): Client Components có thể xử lý các sự kiện như nhấp chuột (onClick) hoặc thay đổi giá trị (onChange), giúp tạo ra các tương tác động trên giao diện người dùng.
-
Sử dụng React state và lifecycle effects (useState, useEffect): Chúng có thể sử dụng các hook như
useState
để quản lý trạng thái vàuseEffect
để thực hiện các tác vụ phụ khi component được render hoặc cập nhật. -
Truy cập các API chỉ có trên trình duyệt (window, localStorage): Client Components có thể truy cập các API đặc thù của trình duyệt như
window
hoặclocalStorage
, điều mà Server Components không thể làm được. - Sử dụng custom hooks phụ thuộc vào state hoặc browser APIs: Chúng có thể sử dụng các custom hooks mà phụ thuộc vào trạng thái hoặc các API của trình duyệt, giúp tái sử dụng logic một cách hiệu quả.
- Sử dụng React class components: Client Components cũng có thể sử dụng các class components của React, mặc dù functional components với hooks đã trở nên phổ biến hơn.
Lưu ý: Các Client Components cần có chỉ thị "use client"
ở đầu file để báo cho Next.js biết rằng chúng nên được chạy trên client. Điều này giúp phân biệt giữa các thành phần chạy trên máy chủ và các thành phần chạy trên trình duyệt.
Chia sẻ dữ liệu giữa các Components
Khi nhiều Server Components cần cùng một dữ liệu, bạn không cần phải truyền dữ liệu thông qua props hoặc sử dụng React Context. Thay vào đó, mỗi component có thể tự fetch dữ liệu mà nó cần một cách độc lập.
- Next.js tự động memoize các lệnh fetch: Nếu nhiều component yêu cầu cùng một dữ liệu, Next.js sẽ đảm bảo rằng lệnh fetch thực tế chỉ được thực hiện một lần. Điều này giúp tối ưu hiệu suất và tránh lãng phí tài nguyên.
-
Đối với các hàm không phải fetch: Bạn có thể sử dụng hàm
cache
của React để đạt được hiệu quả tương tự (memoization).
Giữ mã Server Code tránh xa Client
Đôi khi, các đoạn mã chỉ dành cho server (như truy cập biến môi trường riêng tư) có thể vô tình được đưa vào các gói client (client bundles). Để ngăn chặn điều này, bạn có thể sử dụng gói server-only
.
// In your server-only module
import "server-only";
export async function getData() {
// Code that should only run on the server
const res = await fetch("https://api.example.com", {
headers: { authorization: process.env.API_KEY },
});
return res.json();
}
Nếu bất kỳ Client Component nào cố gắng import module này, bạn sẽ nhận được lỗi build-time error, giúp đảm bảo rằng mã server không bao giờ bị rò rỉ vào client.
Sử dụng Third-Party Components (Các thành phần từ bên thứ ba)
Nhiều thư viện bên thứ ba chưa thêm chỉ thị "use client"
mặc dù chúng sử dụng các tính năng chỉ dành cho client. Điều này khiến chúng hoạt động trong Client Components nhưng sẽ gây lỗi trong Server Components.
Giải pháp: Bọc các thành phần bên thứ ba trong một Client Component của riêng bạn. Bây giờ, bạn có thể sử dụng thành phần đã bọc này trong Server Components.
// carousel.tsx
"use client";
import { Carousel } from "acme-carousel";
export default Carousel;
Sử dụng Context Providers
React Context không hoạt động trong Server Components. Để sử dụng context, bạn cần tạo provider trong một Client Component. Sau đó, sử dụng provider này trong layout của Server Component.
// theme-provider.tsx
"use client";
import { createContext } from "react";
export const ThemeContext = createContext({});
export default function ThemeProvider({ children }) {
return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>;
}
Sau đó sử dụng ở Server Component Layout
// layout.tsx
import ThemeProvider from "./theme-provider";
export default function Layout({ children }) {
return (
<html>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}
Client Component Patterns
1. Di chuyển Client Components xuống dưới cây component
Để giảm lượng JavaScript gửi đến trình duyệt, hãy di chuyển các phần tương tác vào các Client Components riêng biệt thay vì biến toàn bộ layout hoặc trang thành Client Components.
Ví dụ: Giữ layout là một Server Component, nhưng biến thanh tìm kiếm tương tác thành một Client Component.
2. Truyền Props từ Server Components sang Client Components
Bạn có thể truyền dữ liệu từ Server Components sang Client Components, nhưng các props phải có thể serializable (có thể chuyển đổi thành JSON). Các đối tượng phức tạp như hàm, ngày tháng hoặc lớp sẽ không hoạt động.
Nếu bạn cần dữ liệu không thể serializable, hãy fetch trực tiếp trong Client Component hoặc sử dụng Route Handler.
Interleaving Server and Client Components
Bạn có thể hình dung giao diện người dùng của mình như một cây bắt đầu từ layout gốc (một Server Component). Một số nhánh cây có thể được render trên client bằng cách thêm "use client"
.
Trong các nhánh client, bạn vẫn có thể bao gồm Server Components, nhưng cần tuân theo các quy tắc quan trọng.
Pattern to Avoid: Importing Server Components vào Client Components
Điều này không hoạt động: Bạn không thể import một Server Component vào Client Component. Thay vào đó, hãy sử dụng pattern truyền Server Components dưới dạng props.
"use client";
// ❌ Error: You cannot import a Server Component into a Client Component
import ServerComponent from "./server-component";
export default function ClientComponent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
<ServerComponent />
</>
);
}
Supported Pattern: Truyền Server Components dưới dạng Props
// In client-component.tsx
'use client'
export default function ClientComponent({ children }) {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
{children}
</>
)
}
// In page.tsx (a Server Component)
import ClientComponent from './client-component'
import ServerComponent from './server-component'
export default function Page() {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
)
}
Đây là cách làm đúng: Bạn có thể truyền Server Components vào Client Components thông qua props như children
. Cách tiếp cận này cho phép cả hai component render độc lập: Server Component render trên server trước, và Client Component render trên client sau.
Bạn không bị giới hạn ở prop children
. Bạn có thể sử dụng bất kỳ prop nào để truyền nội dung Server Component vào Client Component.
Top comments (0)