Bài viết này nhận được sự hỗ trợ nhiệt tình của những người bạn thân thiết của chúng tôi tại Sentry.io, những người giúp các nhà phát triển nhìn thấy những gì thực sự quan trọng, giải quyết nhanh hơn và liên tục tìm hiểu về ứng dụng của họ. Cảm ơn các bạn!

Trong bài viết này, chúng ta sẽ đi sâu vào React Server Components (RSC). Chúng là cải tiến mới nhất trong hệ sinh thái của React, tận dụng cả kết xuất phía máy chủ và phía máy khách cũng như phát trực tuyến HTML để phân phối nội dung nhanh nhất có thể.
Chúng ta sẽ thực sự tìm hiểu sâu để hiểu đầy đủ về cách RSC phù hợp với bức tranh React, mức độ kiểm soát mà chúng cung cấp đối với vòng đời kết xuất của các thành phần và tải trang trông như thế nào khi có RSC.
Nhưng trước khi đi sâu vào tất cả những điều đó, tôi nghĩ rằng chúng ta nên nhìn lại cách React đã kết xuất các trang web cho đến thời điểm này để đặt bối cảnh cho lý do tại sao chúng ta cần RSC ngay từ đầu.
HTML trả về từ máy chủ chứa một số thứ, bao gồm:

Ứng dụng web trong quy trình này chỉ tương tác hoàn toàn khi JavaScript hoàn tất các hoạt động của nó. Có lẽ bạn đã thấy được sự căng thẳng ở đây đi kèm với trải nghiệm của nhà phát triển (DX) được cải thiện nhưng lại tác động tiêu cực đến trải nghiệm của người dùng (UX).
Sự thật là có (và vẫn đang) những ưu và nhược điểm của CSR trong React. Nhìn vào những mặt tích cực, các ứng dụng web cung cấp chuyển đổi mượt mà, nhanh chóng giúp giảm tổng thời gian tải trang, nhờ các thành phần phản ứng cập nhật theo tương tác của người dùng mà không kích hoạt làm mới trang. CSR làm giảm tải máy chủ và cho phép chúng tôi phục vụ các tài sản từ các mạng phân phối nội dung (CDN) tốc độ cao có khả năng phân phối nội dung đến người dùng từ vị trí máy chủ gần người dùng hơn về mặt địa lý để tải trang được tối ưu hóa hơn nữa.
CSR cũng có những hậu quả không mấy tốt đẹp, đáng chú ý nhất có lẽ là các thành phần có thể truy xuất dữ liệu độc lập, dẫn đến các yêu cầu mạng thác nước làm chậm mọi thứ đáng kể. Điều này có vẻ như là một phiền toái nhỏ đối với khía cạnh UX, nhưng thiệt hại thực sự có thể khá lớn đối với con người. “Sức khỏe hiện đại, khuôn khổ, hiệu suất và tác hại” của Eric Bailey nên là một câu chuyện cảnh báo cho tất cả các công việc CSR.
Những hậu quả tiêu cực khác của CSR không quá nghiêm trọng nhưng vẫn dẫn đến thiệt hại. Ví dụ, trước đây, một tài liệu HTML chỉ chứa siêu dữ liệu và
Vâng, React đã đạt được khả năng kết xuất phía máy chủ (SSR). Vào một thời điểm nào đó, SSR đã trở thành chủ đề nóng trong cộng đồng React đến mức nó đã có một khoảnh khắc trở thành tâm điểm chú ý. Việc chuyển sang SSR đã mang lại những thay đổi đáng kể cho quá trình phát triển ứng dụng, cụ thể là cách nó ảnh hưởng đến hành vi của React và cách nội dung có thể được phân phối thông qua máy chủ thay vì trình duyệt.

Kết xuất phía máy chủ cũng khắc phục các sự cố SEO đi kèm với CSR. Vì trình thu thập dữ liệu nhận được nội dung của trang web của chúng tôi trực tiếp nên chúng có thể lập chỉ mục ngay lập tức. Việc truy xuất dữ liệu ban đầu cũng diễn ra trên máy chủ, đây là một điểm cộng vì nó gần với nguồn dữ liệu hơn và có thể loại bỏ thác nước truy xuất nếu được thực hiện đúng cách.
Bạn còn nhớ khi tôi đề cập rằng SSR thường cải thiện số liệu hiệu suất FCP không? Điều đó có thể đúng, nhưng số liệu hiệu suất Thời gian đến byte đầu tiên (TTFB) đã bị ảnh hưởng tiêu cực với SSR. Trình duyệt thực sự phải đợi máy chủ tìm nạp dữ liệu cần thiết, tạo HTML ban đầu và gửi byte đầu tiên. Và mặc dù TTFB không phải là số liệu Core Web Vital tự thân, nhưng nó ảnh hưởng đến các số liệu. Một TTFB âm dẫn đến các số liệu Core Web Vitals âm.
Một nhược điểm khác của SSR là toàn bộ trang không phản hồi cho đến khi React phía máy khách hoàn tất việc cấp nước cho trang. Các thành phần tương tác không thể lắng nghe và "phản ứng" với các tương tác của người dùng trước khi React cấp nước cho chúng, tức là React gắn các trình lắng nghe sự kiện dự định vào chúng. Quá trình cấp nước thường diễn ra nhanh, nhưng kết nối internet và khả năng phần cứng của thiết bị đang sử dụng có thể làm chậm quá trình kết xuất đáng kể.
Chúng ta sẽ xem xét hai phiên bản đầu tiên — tạo trang tĩnh và tái tạo tĩnh gia tăng — trước khi chuyển sang toàn bộ cuộc thảo luận về React Server Components, phiên bản thứ ba.
Như bạn có thể nghi ngờ, phương pháp kết xuất kết hợp này phù hợp với các dự án nhỏ hơn, trong đó nội dung không thay đổi nhiều, như trang web tiếp thị hoặc blog cá nhân, trái ngược với các dự án lớn hơn, trong đó nội dung có thể thay đổi theo tương tác của người dùng, như trang web thương mại điện tử.
SSG giảm gánh nặng cho máy chủ đồng thời cải thiện số liệu hiệu suất liên quan đến TTFB vì máy chủ không còn phải thực hiện các tác vụ nặng, tốn kém để kết xuất lại trang.
Nhóm Next.js đã tạo ra hương vị lai thứ hai của React để giải quyết nhược điểm của việc xây dựng lại SSG hoàn chỉnh: tái tạo tĩnh gia tăng (ISR). Tên nói lên rất nhiều về cách tiếp cận ở chỗ ISR chỉ xây dựng lại những gì cần thiết thay vì toàn bộ. Chúng tôi tạo "phiên bản ban đầu" của trang một cách tĩnh trong thời gian xây dựng nhưng cũng có thể xây dựng lại bất kỳ trang nào chứa dữ liệu cũ sau khi người dùng truy cập vào trang đó (tức là yêu cầu của máy chủ kích hoạt kiểm tra dữ liệu).
Từ thời điểm đó, máy chủ sẽ phục vụ các phiên bản mới của trang đó một cách tĩnh theo từng gia số khi cần. Điều đó khiến ISR trở thành một cách tiếp cận lai nằm gọn giữa SSG và SSR truyền thống.
Đồng thời, ISR không giải quyết được triệu chứng "nội dung cũ", khi người dùng có thể truy cập một trang trước khi trang đó được tạo xong. Không giống như SSG, ISR cần một máy chủ thực tế để tạo lại các trang riêng lẻ để phản hồi trình duyệt của người dùng thực hiện yêu cầu máy chủ. Điều đó có nghĩa là chúng ta mất đi khả năng có giá trị để triển khai các ứng dụng dựa trên ISR trên CDN để phân phối tài sản được tối ưu hóa.
RSC có thể giảm đáng kể lượng JavaScript được chuyển đến máy khách vì chúng tôi có thể quyết định chọn lọc thành phần nào sẽ phục vụ tĩnh trên máy chủ và thành phần nào sẽ kết xuất ở phía máy khách. Có nhiều khả năng kiểm soát và linh hoạt hơn để đạt được sự cân bằng phù hợp cho dự án cụ thể của bạn.

Thuật ngữ "Client Component" không mô tả bất kỳ điều gì mới; chúng chỉ được gắn nhãn để giúp phân biệt các thành phần CSR "cũ" với Server Components. Client Components được định nghĩa bằng lệnh
Trong Next.js, tất cả các thành phần đều là Server Components theo mặc định. Đó là lý do tại sao chúng ta cần định nghĩa rõ ràng Client Components của mình bằng
Bạn có thể (đúng) cho rằng Client Components chỉ được render trên client, nhưng Next.js render Client Components trên server để tạo HTML ban đầu. Do đó, trình duyệt có thể ngay lập tức bắt đầu render chúng và sau đó thực hiện hydrat hóa sau.
Nhưng hãy đợi đã! Trên thực tế, chúng ta có thể nhập Server Components vào Client Components. Chỉ là không phải là mối quan hệ một-một trực tiếp vì Server Component sẽ được chuyển đổi thành Client Component. Nếu bạn đang sử dụng server API mà bạn không thể sử dụng trong trình duyệt, bạn sẽ nhận được lỗi; nếu không — bạn sẽ có một Thành phần Máy chủ có mã bị "rò rỉ" tới trình duyệt.
Đây là một sắc thái cực kỳ quan trọng cần ghi nhớ khi bạn làm việc với RSC.

Chúng ta sẽ xem hoạt động này diễn ra như thế nào từ góc nhìn của trình duyệt sau một chút.
Để tìm mã này trong ứng dụng demo, hãy mở công cụ dành cho nhà phát triển của trình duyệt tại tab Elements và xem
Mỗi dòng trong đoạn trích trên là một tải trọng RSC riêng lẻ. Bạn có thể thấy rằng mỗi dòng bắt đầu bằng một số hoặc một chữ cái, theo sau là dấu hai chấm, rồi đến một mảng đôi khi được thêm tiền tố là các chữ cái. Chúng ta sẽ không đi sâu vào chi tiết về ý nghĩa của chúng, nhưng nói chung:
Máy chủ trả về tiêu đề

Chúng ta cũng có thể gỡ lỗi cách Next.js gửi các khối trong thiết bị đầu cuối bằng lệnh

Bạn có thể thấy mô hình này. Đối với mỗi khối, máy chủ phản hồi với kích thước của khối trước khi gửi nội dung của khối. Khi xem kết quả, chúng ta có thể thấy máy chủ đã truyền phát toàn bộ trang thành 16 khối khác nhau. Cuối cùng, máy chủ gửi lại một đoạn có kích thước bằng không, cho biết kết thúc của luồng.
Đoạn đầu tiên bắt đầu bằng khai báo
Mặc dù máy chủ chưa hoàn tất việc truyền phát tài liệu, nhưng các tính năng chịu lỗi của trình duyệt cho phép nó vẽ và gọi bất cứ thứ gì nó có tại thời điểm đó mà không cần chờ các thẻ đóng
Tại thời điểm này, Next.js đã trả về một trang HTML tĩnh đầy đủ bao gồm chính các thành phần (được hiển thị trong HTML tĩnh) hoặc các giá trị dự phòng của chúng (nếu chúng bị treo). Nó lấy tải trọng HTML và RSC tĩnh và truyền chúng trở lại trình duyệt thông qua một hoặc nhiều khối.

Khi các thành phần bị treo tải xong, React tạo HTML đệ quy trong khi tìm kiếm các ranh giới

Trong Hình 7 và 8, hãy lưu ý cách các phần tử dự phòng có ID duy nhất ở dạng
Cùng với đoạn đầu tiên chứa HTML của thành phần bị treo, máy chủ cũng gửi một hàm
Cuối cùng, toàn bộ trang sẽ hoàn tất việc tải, từng phần một.

Vào thời điểm tôi viết bài này, phương pháp động để tải chậm một Thành phần Máy khách trong một Thành phần Máy chủ trong Next.js không hoạt động như bạn mong đợi. Để tải chậm một Thành phần Máy khách một cách hiệu quả, hãy đặt nó vào Thành phần Máy khách “wrapper” sử dụng chính phương thức
Để xem mọi thứ xảy ra trong quá trình tải trang, chúng ta sẽ truy cập tab "Hiệu suất" trong Chrome DevTools và nhấp vào nút "tải lại" để tải lại trang và ghi lại hồ sơ. Đây là giao diện của nó:

Khi chúng ta phóng to ở phần đầu, chúng ta có thể thấy khoảng "Parse HTML" đầu tiên. Đó là máy chủ đang truyền phát các phần đầu tiên của tài liệu đến trình duyệt. Trình duyệt vừa nhận được HTML ban đầu, chứa shell trang và một vài liên kết đến các tài nguyên như phông chữ, tệp CSS và JavaScript. Trình duyệt bắt đầu gọi các tập lệnh.

Sau một thời gian, chúng ta bắt đầu thấy các khung hình đầu tiên của trang xuất hiện, cùng với các tập lệnh JavaScript ban đầu được tải và quá trình hydrat hóa diễn ra. Nếu bạn nhìn kỹ vào khung hình, bạn sẽ thấy toàn bộ vỏ trang được hiển thị và các thành phần "đang tải" được sử dụng tại nơi có các Thành phần Máy chủ bị treo. Bạn có thể nhận thấy rằng điều này diễn ra trong khoảng 800ms, trong khi trình duyệt bắt đầu nhận HTML đầu tiên ở 100ms. Trong 700ms đó, trình duyệt liên tục nhận các đoạn từ máy chủ.
Hãy nhớ rằng đây là ứng dụng demo Next.js chạy cục bộ ở chế độ phát triển, vì vậy nó sẽ chậm hơn so với khi chạy ở chế độ sản xuất.

Chúng ta cũng có thể thấy rằng một Thành phần Khách hàng được tải chậm được phát hiện cùng lúc và nó chứa các tệp CSS và JavaScript cần được truy xuất. Các tệp này không phải là một phần của gói ban đầu vì thành phần không cần thiết cho đến sau này; mã được chia thành các tệp riêng của chúng.
Cách chia tách mã này chắc chắn cải thiện hiệu suất tải trang ban đầu. Nó cũng đảm bảo rằng mã của Thành phần Máy khách chỉ được chuyển đi nếu cần. Nếu Thành phần Máy chủ (hoạt động như thành phần cha của Thành phần Máy khách) trả về lỗi, thì Thành phần Máy khách sẽ không tải. Không hợp lý khi tải toàn bộ mã của nó trước khi chúng ta biết liệu nó có tải được hay không.
Hình 12 cho thấy sự kiện
Tuy nhiên, theo cách này, ứng dụng hoàn toàn tương tác trong năm giây đó. Chúng tôi có thể điều hướng giữa các trang và tương tác với các Thành phần máy khách đã được tải như một phần của gói chính ban đầu. Đây là một chiến thắng thuần túy theo quan điểm trải nghiệm người dùng.
Tôi đã cấu trúc lại cùng một ứng dụng RSC mà tôi đã chia sẻ trước đó để nó sử dụng bộ định tuyến Trang Next.js với SSR. Những cải tiến trong RSC rất đáng kể:

Khi xem hai báo cáo này mà tôi đã lấy từ Sentry, chúng ta có thể thấy rằng phát trực tuyến cho phép trang bắt đầu tải tài nguyên của nó trước khi yêu cầu thực tế hoàn tất. Điều này cải thiện đáng kể số liệu Web Vitals mà chúng ta thấy khi so sánh hai báo cáo.
Kết luận: Người dùng tận hưởng giao diện nhanh hơn, phản ứng tốt hơn với kiến trúc dựa trên RSC.
Kiến trúc RSC giới thiệu hai loại thành phần mới: Thành phần máy chủ và Thành phần máy khách. Sự phân chia này giúp React và các khuôn khổ dựa trên nó — như Next.js — hợp lý hóa việc phân phối nội dung trong khi vẫn duy trì tính tương tác.
Tuy nhiên, thiết lập này cũng đưa ra những thách thức mới trong các lĩnh vực như quản lý trạng thái, xác thực và kiến trúc thành phần. Khám phá những thách thức đó là một chủ đề tuyệt vời cho một bài đăng trên blog khác!
Bất chấp những thách thức này, những lợi ích của RSC là một trường hợp thuyết phục để áp dụng chúng. Chúng ta chắc chắn sẽ thấy các hướng dẫn được xuất bản về cách giải quyết các thách thức của RSC khi chúng hoàn thiện, nhưng theo tôi, chúng đã giống như tương lai của các hoạt động kết xuất trong phát triển web hiện đại.
Trong bài viết này, chúng ta sẽ đi sâu vào React Server Components (RSC). Chúng là cải tiến mới nhất trong hệ sinh thái của React, tận dụng cả kết xuất phía máy chủ và phía máy khách cũng như phát trực tuyến HTML để phân phối nội dung nhanh nhất có thể.
Chúng ta sẽ thực sự tìm hiểu sâu để hiểu đầy đủ về cách RSC phù hợp với bức tranh React, mức độ kiểm soát mà chúng cung cấp đối với vòng đời kết xuất của các thành phần và tải trang trông như thế nào khi có RSC.
Nhưng trước khi đi sâu vào tất cả những điều đó, tôi nghĩ rằng chúng ta nên nhìn lại cách React đã kết xuất các trang web cho đến thời điểm này để đặt bối cảnh cho lý do tại sao chúng ta cần RSC ngay từ đầu.
Những ngày đầu: Kết xuất phía máy khách của React
Các ứng dụng React đầu tiên được kết xuất ở phía máy khách, tức là trong trình duyệt. Với tư cách là nhà phát triển, chúng tôi đã viết các ứng dụng với các lớp JavaScript dưới dạng thành phần và đóng gói mọi thứ bằng các bundler, như Webpack, trong một đống mã được biên dịch đẹp mắt và được sắp xếp theo dạng cây, sẵn sàng để đưa vào môi trường sản xuất.HTML trả về từ máy chủ chứa một số thứ, bao gồm:
- Một tài liệu HTML có siêu dữ liệu trong
và một
trống trong
được sử dụng làm móc để đưa ứng dụng vào DOM;
- Các tài nguyên JavaScript chứa mã lõi của React và mã thực tế cho ứng dụng web, sẽ tạo giao diện người dùng và đưa ứng dụng vào bên trong
trống.

Ứng dụng web trong quy trình này chỉ tương tác hoàn toàn khi JavaScript hoàn tất các hoạt động của nó. Có lẽ bạn đã thấy được sự căng thẳng ở đây đi kèm với trải nghiệm của nhà phát triển (DX) được cải thiện nhưng lại tác động tiêu cực đến trải nghiệm của người dùng (UX).
Sự thật là có (và vẫn đang) những ưu và nhược điểm của CSR trong React. Nhìn vào những mặt tích cực, các ứng dụng web cung cấp chuyển đổi mượt mà, nhanh chóng giúp giảm tổng thời gian tải trang, nhờ các thành phần phản ứng cập nhật theo tương tác của người dùng mà không kích hoạt làm mới trang. CSR làm giảm tải máy chủ và cho phép chúng tôi phục vụ các tài sản từ các mạng phân phối nội dung (CDN) tốc độ cao có khả năng phân phối nội dung đến người dùng từ vị trí máy chủ gần người dùng hơn về mặt địa lý để tải trang được tối ưu hóa hơn nữa.
CSR cũng có những hậu quả không mấy tốt đẹp, đáng chú ý nhất có lẽ là các thành phần có thể truy xuất dữ liệu độc lập, dẫn đến các yêu cầu mạng thác nước làm chậm mọi thứ đáng kể. Điều này có vẻ như là một phiền toái nhỏ đối với khía cạnh UX, nhưng thiệt hại thực sự có thể khá lớn đối với con người. “Sức khỏe hiện đại, khuôn khổ, hiệu suất và tác hại” của Eric Bailey nên là một câu chuyện cảnh báo cho tất cả các công việc CSR.
Những hậu quả tiêu cực khác của CSR không quá nghiêm trọng nhưng vẫn dẫn đến thiệt hại. Ví dụ, trước đây, một tài liệu HTML chỉ chứa siêu dữ liệu và
trống sẽ không thể đọc được đối với trình thu thập thông tin của công cụ tìm kiếm, những trình này sẽ không bao giờ có được trải nghiệm được hiển thị đầy đủ. Mặc dù vấn đề đó đã được giải quyết ngày nay, nhưng cú đánh SEO vào thời điểm đó là một điểm neo trên các trang web của công ty dựa vào lưu lượng truy cập của công cụ tìm kiếm để tạo doanh thu.Sự thay đổi: Hiển thị phía máy chủ (SSR)
Cần phải thay đổi một điều gì đó. CSR đã giới thiệu cho các nhà phát triển một phương pháp tiếp cận mới mạnh mẽ để xây dựng các giao diện tương tác, nhanh chóng, nhưng người dùng ở khắp mọi nơi đều bị ngập trong màn hình trống và các chỉ báo tải để đến đó. Giải pháp là di chuyển trải nghiệm kết xuất từ máy khách sang máy chủ. Tôi biết nghe có vẻ buồn cười khi chúng ta cần cải thiện điều gì đó bằng cách quay lại cách trước đây.Vâng, React đã đạt được khả năng kết xuất phía máy chủ (SSR). Vào một thời điểm nào đó, SSR đã trở thành chủ đề nóng trong cộng đồng React đến mức nó đã có một khoảnh khắc trở thành tâm điểm chú ý. Việc chuyển sang SSR đã mang lại những thay đổi đáng kể cho quá trình phát triển ứng dụng, cụ thể là cách nó ảnh hưởng đến hành vi của React và cách nội dung có thể được phân phối thông qua máy chủ thay vì trình duyệt.

Xử lý các hạn chế của CSR
Thay vì gửi một tài liệu HTML trống với SSR, chúng tôi đã hiển thị HTML ban đầu trên máy chủ và gửi đến trình duyệt. Trình duyệt có thể bắt đầu hiển thị nội dung ngay lập tức mà không cần hiển thị chỉ báo tải. Điều này cải thiện đáng kể chỉ số hiệu suất First Contentful Paint (FCP) trong Web Vitals.Kết xuất phía máy chủ cũng khắc phục các sự cố SEO đi kèm với CSR. Vì trình thu thập dữ liệu nhận được nội dung của trang web của chúng tôi trực tiếp nên chúng có thể lập chỉ mục ngay lập tức. Việc truy xuất dữ liệu ban đầu cũng diễn ra trên máy chủ, đây là một điểm cộng vì nó gần với nguồn dữ liệu hơn và có thể loại bỏ thác nước truy xuất nếu được thực hiện đúng cách.
Hydration
SSR có những phức tạp riêng. Để React biến HTML tĩnh nhận được từ máy chủ thành tương tác, nó cần hydrat hóa nó. Hydration là quá trình xảy ra khi React tái tạo Virtual Document Object Model (DOM) của nó ở phía máy khách dựa trên những gì có trong DOM của HTML ban đầu.Bây giờ chúng ta có hai phiên bản React:Lưu ý: React duy trì Virtual DOM của riêng nó vì nó nhanh hơn trong việc tìm ra các bản cập nhật trên DOM thay vì DOM thực tế. Nó đồng bộ hóa DOM thực với DOM ảo khi cần cập nhật UI nhưng thực hiện thuật toán diffing trên DOM ảo.
- Phiên bản phía máy chủ biết cách hiển thị HTML tĩnh từ cây thành phần của chúng ta,
- Phiên bản phía máy khách biết cách làm cho trang tương tác.
Nhược điểm của SSR
SSR không phải là giải pháp toàn diện giải quyết được các hạn chế của CSR. SSR cũng có những nhược điểm riêng. Kể từ khi chúng tôi di chuyển kết xuất HTML ban đầu và tìm nạp dữ liệu đến máy chủ, các máy chủ đó hiện đang phải chịu tải lớn hơn nhiều so với khi chúng tôi tải mọi thứ trên máy khách.Bạn còn nhớ khi tôi đề cập rằng SSR thường cải thiện số liệu hiệu suất FCP không? Điều đó có thể đúng, nhưng số liệu hiệu suất Thời gian đến byte đầu tiên (TTFB) đã bị ảnh hưởng tiêu cực với SSR. Trình duyệt thực sự phải đợi máy chủ tìm nạp dữ liệu cần thiết, tạo HTML ban đầu và gửi byte đầu tiên. Và mặc dù TTFB không phải là số liệu Core Web Vital tự thân, nhưng nó ảnh hưởng đến các số liệu. Một TTFB âm dẫn đến các số liệu Core Web Vitals âm.
Một nhược điểm khác của SSR là toàn bộ trang không phản hồi cho đến khi React phía máy khách hoàn tất việc cấp nước cho trang. Các thành phần tương tác không thể lắng nghe và "phản ứng" với các tương tác của người dùng trước khi React cấp nước cho chúng, tức là React gắn các trình lắng nghe sự kiện dự định vào chúng. Quá trình cấp nước thường diễn ra nhanh, nhưng kết nối internet và khả năng phần cứng của thiết bị đang sử dụng có thể làm chậm quá trình kết xuất đáng kể.
The Present: A Hybrid Approach
Cho đến nay, chúng ta đã đề cập đến hai phương pháp kết xuất React khác nhau: CSR và SSR. Mặc dù cả hai đều là những nỗ lực để cải thiện lẫn nhau, nhưng giờ đây chúng ta có được những điều tốt nhất của cả hai thế giới, có thể nói như vậy, vì SSR đã phân nhánh thành ba phiên bản React bổ sung cung cấp phương pháp tiếp cận kết hợp với hy vọng giảm thiểu những hạn chế đi kèm với CSR và SSR.Chúng ta sẽ xem xét hai phiên bản đầu tiên — tạo trang tĩnh và tái tạo tĩnh gia tăng — trước khi chuyển sang toàn bộ cuộc thảo luận về React Server Components, phiên bản thứ ba.
Tạo trang tĩnh (SSG)
Thay vì tạo lại cùng một mã HTML cho mọi yêu cầu, chúng tôi đã đưa ra SSG. Phiên bản React này biên dịch và xây dựng toàn bộ ứng dụng tại thời điểm xây dựng, tạo các tệp tĩnh (như trong HTML và CSS nguyên bản) sau đó được lưu trữ trên CDN tốc độ cao.Như bạn có thể nghi ngờ, phương pháp kết xuất kết hợp này phù hợp với các dự án nhỏ hơn, trong đó nội dung không thay đổi nhiều, như trang web tiếp thị hoặc blog cá nhân, trái ngược với các dự án lớn hơn, trong đó nội dung có thể thay đổi theo tương tác của người dùng, như trang web thương mại điện tử.
SSG giảm gánh nặng cho máy chủ đồng thời cải thiện số liệu hiệu suất liên quan đến TTFB vì máy chủ không còn phải thực hiện các tác vụ nặng, tốn kém để kết xuất lại trang.
Tái tạo tĩnh gia tăng (ISR)
Một nhược điểm của SSG là phải xây dựng lại toàn bộ mã của ứng dụng khi cần thay đổi nội dung. Nội dung được cố định — là tĩnh và tất cả — và không có cách nào để thay đổi chỉ một phần của nó mà không phải xây dựng lại toàn bộ.Nhóm Next.js đã tạo ra hương vị lai thứ hai của React để giải quyết nhược điểm của việc xây dựng lại SSG hoàn chỉnh: tái tạo tĩnh gia tăng (ISR). Tên nói lên rất nhiều về cách tiếp cận ở chỗ ISR chỉ xây dựng lại những gì cần thiết thay vì toàn bộ. Chúng tôi tạo "phiên bản ban đầu" của trang một cách tĩnh trong thời gian xây dựng nhưng cũng có thể xây dựng lại bất kỳ trang nào chứa dữ liệu cũ sau khi người dùng truy cập vào trang đó (tức là yêu cầu của máy chủ kích hoạt kiểm tra dữ liệu).
Từ thời điểm đó, máy chủ sẽ phục vụ các phiên bản mới của trang đó một cách tĩnh theo từng gia số khi cần. Điều đó khiến ISR trở thành một cách tiếp cận lai nằm gọn giữa SSG và SSR truyền thống.
Đồng thời, ISR không giải quyết được triệu chứng "nội dung cũ", khi người dùng có thể truy cập một trang trước khi trang đó được tạo xong. Không giống như SSG, ISR cần một máy chủ thực tế để tạo lại các trang riêng lẻ để phản hồi trình duyệt của người dùng thực hiện yêu cầu máy chủ. Điều đó có nghĩa là chúng ta mất đi khả năng có giá trị để triển khai các ứng dụng dựa trên ISR trên CDN để phân phối tài sản được tối ưu hóa.
Tương lai: Các thành phần máy chủ React
Cho đến thời điểm này, chúng ta đã cân nhắc giữa các phương pháp tiếp cận CSR, SSR, SSG và ISR, trong đó tất cả đều có một số loại đánh đổi, ảnh hưởng tiêu cực đến hiệu suất, độ phức tạp của quá trình phát triển và trải nghiệm của người dùng. React Server Components (RSC) mới ra mắt nhằm mục đích giải quyết hầu hết những nhược điểm này bằng cách cho phép chúng tôi — nhà phát triển — chọn chiến lược kết xuất phù hợp cho từng thành phần React riêng lẻ.RSC có thể giảm đáng kể lượng JavaScript được chuyển đến máy khách vì chúng tôi có thể quyết định chọn lọc thành phần nào sẽ phục vụ tĩnh trên máy chủ và thành phần nào sẽ kết xuất ở phía máy khách. Có nhiều khả năng kiểm soát và linh hoạt hơn để đạt được sự cân bằng phù hợp cho dự án cụ thể của bạn.
Nhưng chính xác thì RSC là gì? Hãy cùng phân tích một thành phần để xem nó hoạt động như thế nào.Lưu ý: Điều quan trọng cần lưu ý là khi chúng tôi áp dụng các kiến trúc tiên tiến hơn, như RSC, các giải pháp giám sát trở nên vô giá. Sentry cung cấp khả năng giám sát hiệu suất và theo dõi lỗi mạnh mẽ giúp bạn theo dõi hiệu suất thực tế của ứng dụng chạy bằng RSC. Sentry cũng giúp bạn có được thông tin chi tiết về hiệu suất của bản phát hành và mức độ ổn định của chúng, đây là một tính năng quan trọng khác cần có khi di chuyển các ứng dụng hiện tại của bạn sang RSC. Việc triển khai Sentry trong một khuôn khổ hỗ trợ RSC như Next.js dễ dàng như chạy một lệnh đầu cuối duy nhất.
Giải phẫu của React Server Components
Phương pháp tiếp cận mới này giới thiệu hai loại thành phần kết xuất: Server Components và Client Components. Sự khác biệt giữa hai thành phần này không phải là cách chúng hoạt động mà là nơi chúng thực thi và môi trường chúng được thiết kế cho. Tại thời điểm viết bài này, cách duy nhất để sử dụng RSC là thông qua các khuôn khổ React. Và hiện tại, chỉ có ba khuôn khổ hỗ trợ chúng: Next.js, Gatsby và RedwoodJS.
Thành phần máy chủ
Thành phần máy chủ được thiết kế để thực thi trên máy chủ và mã của chúng không bao giờ được chuyển đến trình duyệt. Đầu ra HTML và bất kỳ thuộc tính nào chúng có thể chấp nhận là những phần duy nhất được phục vụ. Cách tiếp cận này có nhiều lợi ích về hiệu suất và cải tiến trải nghiệm người dùng:- Thành phần máy chủ cho phép các phụ thuộc lớn vẫn ở phía máy chủ.
Hãy tưởng tượng sử dụng một thư viện lớn cho một thành phần. Nếu bạn đang thực thi thành phần ở phía máy khách, điều đó có nghĩa là bạn cũng đang chuyển toàn bộ thư viện đến trình duyệt. Với Server Components, bạn chỉ lấy đầu ra HTML tĩnh và tránh phải gửi bất kỳ JavaScript nào đến trình duyệt. Server Components thực sự tĩnh và chúng loại bỏ toàn bộ bước hydrat hóa. - Server Components nằm gần hơn nhiều với các nguồn dữ liệu — ví dụ: cơ sở dữ liệu hoặc hệ thống tệp — mà chúng cần để tạo mã.
Chúng cũng tận dụng sức mạnh tính toán của máy chủ để tăng tốc các tác vụ kết xuất tốn nhiều tài nguyên tính toán và chỉ gửi kết quả đã tạo trở lại máy khách. Chúng cũng được tạo trong một lần chạy duy nhất, tránh thác yêu cầu và các chuyến đi khứ hồi HTTP. - Các thành phần máy chủ lưu trữ dữ liệu và logic nhạy cảm một cách an toàn khỏi trình duyệt.
Điều đó là nhờ vào thực tế là các mã thông báo cá nhân và khóa API được thực thi trên một máy chủ an toàn thay vì máy khách. - Kết quả kết xuất có thể được lưu vào bộ nhớ đệm và sử dụng lại giữa các yêu cầu tiếp theo và thậm chí trên các phiên khác nhau.
Điều này làm giảm đáng kể thời gian kết xuất cũng như tổng lượng dữ liệu được truy xuất cho mỗi yêu cầu.
cung cấp giá trị dự phòng. Khung triển khai sử dụng dự phòng ban đầu nhưng truyền phát nội dung mới tạo khi nội dung đó đã sẵn sàng. Chúng ta sẽ nói thêm về truyền phát, nhưng trước tiên hãy xem xét Client Components và so sánh chúng với Server Components.Client Components
Client Components là các thành phần mà chúng ta đã biết và yêu thích. Chúng được thực thi ở phía máy khách. Do đó, Client Components có khả năng xử lý các tương tác của người dùng và có quyền truy cập vào các API của trình duyệt nhưlocalStorage
và định vị địa lý.Thuật ngữ "Client Component" không mô tả bất kỳ điều gì mới; chúng chỉ được gắn nhãn để giúp phân biệt các thành phần CSR "cũ" với Server Components. Client Components được định nghĩa bằng lệnh
"use client"
ở đầu các tệp của chúng.
Mã:
"use client"export default function LikeButton() { const likePost = () => { // ... } return ( Like )}
"use client"
. Ngoài ra còn có lệnh "use server"
, nhưng lệnh này được sử dụng cho Server Actions (là các hành động giống như RPC được gọi từ máy khách nhưng được thực thi trên máy chủ). Bạn không sử dụng lệnh này để định nghĩa Server Components của mình.Bạn có thể (đúng) cho rằng Client Components chỉ được render trên client, nhưng Next.js render Client Components trên server để tạo HTML ban đầu. Do đó, trình duyệt có thể ngay lập tức bắt đầu render chúng và sau đó thực hiện hydrat hóa sau.
Mối quan hệ giữa Server Components và Client Components
Client Components chỉ có thể rõ ràng import các Client Components khác. Nói cách khác, chúng ta không thể import Server Component vào Client Component do sự cố render lại. Nhưng chúng ta có thể có Server Components trong cây con của Client Component — chỉ được truyền qua propchildren
. Vì Client Components nằm trong trình duyệt và chúng xử lý tương tác của người dùng hoặc xác định trạng thái riêng của chúng, nên chúng thường được render lại. Khi Client Component render lại, cây con của nó cũng sẽ render lại. Nhưng nếu cây con của nó chứa Server Components, chúng sẽ render lại như thế nào? Chúng không sống ở phía máy khách. Đó là lý do tại sao nhóm React đưa ra giới hạn đó.Nhưng hãy đợi đã! Trên thực tế, chúng ta có thể nhập Server Components vào Client Components. Chỉ là không phải là mối quan hệ một-một trực tiếp vì Server Component sẽ được chuyển đổi thành Client Component. Nếu bạn đang sử dụng server API mà bạn không thể sử dụng trong trình duyệt, bạn sẽ nhận được lỗi; nếu không — bạn sẽ có một Thành phần Máy chủ có mã bị "rò rỉ" tới trình duyệt.
Đây là một sắc thái cực kỳ quan trọng cần ghi nhớ khi bạn làm việc với RSC.
Vòng đời Kết xuất
Sau đây là thứ tự các hoạt động mà Next.js thực hiện để truyền phát nội dung:- Bộ định tuyến ứng dụng khớp URL của trang với một Thành phần Máy chủ, xây dựng cây thành phần và hướng dẫn React phía máy chủ kết xuất Thành phần Máy chủ đó và tất cả các thành phần con của nó.
- Trong quá trình kết xuất, React tạo ra "Tải trọng RSC". RSC Payload thông báo cho Next.js về trang và những gì mong đợi đáp lại, cũng như những gì cần quay lại trong
.
- Nếu React gặp một thành phần bị treo, nó sẽ tạm dừng việc hiển thị cây con đó và sử dụng giá trị dự phòng của thành phần bị treo.
- Khi React lặp qua thành phần tĩnh cuối cùng, Next.js sẽ chuẩn bị HTML được tạo và RSC Payload trước khi truyền phát trở lại máy khách thông qua một hoặc nhiều khối.
- Sau đó, React phía máy khách sử dụng các hướng dẫn dành cho RSC Payload và các thành phần phía máy khách để hiển thị UI. Nó cũng cung cấp nước cho từng Thành phần Máy khách khi chúng tải.
- Máy chủ truyền phát trong các Thành phần Máy chủ bị treo khi chúng trở nên khả dụng dưới dạng RSC Payload. Các thành phần con của Thành phần Máy khách cũng được cung cấp nước tại thời điểm này nếu thành phần bị treo chứa bất kỳ thành phần nào.

Chúng ta sẽ xem hoạt động này diễn ra như thế nào từ góc nhìn của trình duyệt sau một chút.
Tải trọng RSC
Tải trọng RSC là định dạng dữ liệu đặc biệt mà máy chủ tạo ra khi kết xuất cây thành phần và bao gồm sau đây:- HTML được kết xuất,
- Placeholders nơi các Thành phần Máy khách sẽ được kết xuất,
- References to the Client Components’ JavaScript files,
- Directions on which JavaScript files it should invoke,
- Any props passed from a Server Component to a Client Component.
Mã:
1:HL["/_next/static/media/c9a5bc6a7c948fb0-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]2:HL["/_next/static/css/app/layout.css?v=1711137019097","style"]0:"$L3"4:HL["/_next/static/css/app/page.css?v=1711137019097","style"]5:I["(app-page s-browser)/./node_modules/next/dist/client/components/app-router.js",["app-pages-internals","static/chunks/app-pages-internals.js"],""]8:"$Sreact.suspense"a:I["(app-pages-browser)/./node_modules/next/dist/client/components/layout-router.js",["app-pages-internals","static/chunks /app-pages-internals.js"],""]b:I["(app-pages-browser)/./node_modules/next/dist/client/components/render-from-template-context.js",["app-pages-internals","static/chunks/app-pages-internals.js"],""]d:I["(app-pages-browser)/./src/app/global-error.jsx",["app/global-error","static/c hunks/app/global-error.js"],""]f:I["(app-pages-browser)/./src/components/clearCart.js",["app/page","static/chunks/app/page.js"],"ClearCart"]7:["$","main",null,{"className":"page_main__GlU4n","children":[["$","$Lf",null,{}],["$","$8",null,{"fallback":["$","p",null,{"children":"🌀 đang tải sản phẩm..."}],"children":"$L10"}]]}]c:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]...9:["$","p",null,{"children":["🛍️ ",3]}]11:I["(app-pages-browser)/./src/components/addToCart.js",["app/page","static/chunks/app/page.js"],"AddToCart"]10:["$","ul",null,{"children":[["$","li","1",{"children":["Gloves"," - $",20,["$...
thẻ ở cuối trang. Chúng sẽ chứa các dòng như:
Mã:
self.__next_f.push([1,"PAYLOAD_STRING_HERE"]).
-
Tải trọng HL
được gọi là "gợi ý" và liên kết đến các tài nguyên cụ thể như CSS và phông chữ. -
Tải trọng I
được gọi là "mô-đun" và chúng gọi các tập lệnh cụ thể. Đây cũng là cách các Thành phần Máy khách được tải. Nếu Thành phần Máy khách là một phần của gói chính, nó sẽ thực thi. Nếu không (tức là tải chậm), một tập lệnh truy xuất được thêm vào gói chính để truy xuất các tệp CSS và JavaScript của thành phần khi cần hiển thị. Sẽ có một tải trọngI
được gửi từ máy chủ để gọi tập lệnh truy xuất khi cần. -
Tải trọng "$"
là các định nghĩa DOM được tạo cho một Thành phần Máy chủ nhất định. Chúng thường đi kèm với HTML tĩnh thực tế được truyền phát từ máy chủ. Đó là những gì xảy ra khi một thành phần bị treo sẵn sàng để hiển thị: máy chủ tạo HTML tĩnh và Tải trọng RSC của nó, sau đó truyền phát cả hai đến trình duyệt.
Truyền phát
Truyền phát cho phép chúng ta hiển thị dần dần UI từ máy chủ. Với RSC, mỗi thành phần có khả năng truy xuất dữ liệu của riêng nó. Một số thành phần hoàn toàn tĩnh và sẵn sàng để gửi ngay đến máy khách, trong khi những thành phần khác cần nhiều công việc hơn trước khi tải. Dựa trên điều này, Next.js chia công việc đó thành nhiều phần và truyền phát chúng đến trình duyệt khi chúng đã sẵn sàng. Vì vậy, khi người dùng truy cập một trang, máy chủ sẽ gọi tất cả các Thành phần Máy chủ, tạo HTML ban đầu cho trang (tức là shell trang), thay thế nội dung của các thành phần "bị treo" bằng các thành phần dự phòng của chúng và truyền phát tất cả những nội dung đó qua một hoặc nhiều phần trở lại máy khách.Máy chủ trả về tiêu đề
Transfer-Encoding: chunked
cho phép trình duyệt biết để mong đợi HTML truyền phát. Điều này chuẩn bị cho trình duyệt để nhận nhiều phần của tài liệu, hiển thị chúng khi nhận được. Trên thực tế, chúng ta có thể thấy tiêu đề khi mở Công cụ dành cho nhà phát triển tại tab Mạng. Kích hoạt làm mới và nhấp vào yêu cầu tài liệu.
Chúng ta cũng có thể gỡ lỗi cách Next.js gửi các khối trong thiết bị đầu cuối bằng lệnh
curl
:
Mã:
curl -D - --raw localhost:3000 > chunked-response.txt

Bạn có thể thấy mô hình này. Đối với mỗi khối, máy chủ phản hồi với kích thước của khối trước khi gửi nội dung của khối. Khi xem kết quả, chúng ta có thể thấy máy chủ đã truyền phát toàn bộ trang thành 16 khối khác nhau. Cuối cùng, máy chủ gửi lại một đoạn có kích thước bằng không, cho biết kết thúc của luồng.
Đoạn đầu tiên bắt đầu bằng khai báo
. Trong khi đó, đoạn thứ hai từ cuối chứa các thẻ đóng
và
. Vì vậy, chúng ta có thể thấy rằng máy chủ truyền phát toàn bộ tài liệu từ trên xuống dưới, sau đó tạm dừng để chờ các thành phần bị treo và cuối cùng, ở phần cuối, đóng phần thân và HTML trước khi dừng truyền phát.Mặc dù máy chủ chưa hoàn tất việc truyền phát tài liệu, nhưng các tính năng chịu lỗi của trình duyệt cho phép nó vẽ và gọi bất cứ thứ gì nó có tại thời điểm đó mà không cần chờ các thẻ đóng
và
.Các thành phần bị treo
Chúng ta đã học được từ vòng đời kết xuất rằng khi một trang được truy cập, Next.js sẽ khớp với thành phần RSC cho trang đó và yêu cầu React hiển thị cây con của nó trong HTML. Khi React tình cờ gặp một thành phần bị treo (tức là thành phần hàm async), nó sẽ lấy giá trị dự phòng của thành phần đó từ thành phần
(hoặc tệp loading.js
nếu đó là tuyến Next.js), hiển thị tệp đó thay vào đó, sau đó tiếp tục tải các thành phần khác. Trong khi đó, RSC gọi thành phần async ở chế độ nền, được truyền phát sau khi hoàn tất tải.Tại thời điểm này, Next.js đã trả về một trang HTML tĩnh đầy đủ bao gồm chính các thành phần (được hiển thị trong HTML tĩnh) hoặc các giá trị dự phòng của chúng (nếu chúng bị treo). Nó lấy tải trọng HTML và RSC tĩnh và truyền chúng trở lại trình duyệt thông qua một hoặc nhiều khối.

Khi các thành phần bị treo tải xong, React tạo HTML đệ quy trong khi tìm kiếm các ranh giới
lồng nhau khác, tạo tải trọng RSC của chúng và sau đó cho phép Next.js truyền tải Tải trọng HTML và RSC trở lại trình duyệt dưới dạng các khối mới. Khi trình duyệt nhận được các khối mới, nó có tải trọng HTML và RSC cần thiết và sẵn sàng thay thế phần tử dự phòng từ DOM bằng HTML mới được truyền phát. Và cứ thế.
Trong Hình 7 và 8, hãy lưu ý cách các phần tử dự phòng có ID duy nhất ở dạng
B:0
, B:1
, v.v., trong khi các thành phần thực tế có ID tương tự ở dạng tương tự: S:0
và S:1
, v.v. on.Cùng với đoạn đầu tiên chứa HTML của thành phần bị treo, máy chủ cũng gửi một hàm
$RC
(tức là completeBoundary
từ mã nguồn của React) biết cách tìm phần tử dự phòng B:0
trong DOM và thay thế nó bằng mẫu S:0
mà nó nhận được từ máy chủ. Đó là hàm "thay thế" cho phép chúng ta xem nội dung thành phần khi chúng xuất hiện trong trình duyệt.Cuối cùng, toàn bộ trang sẽ hoàn tất việc tải, từng phần một.
Thành phần tải chậm
Nếu Thành phần Máy chủ bị treo chứa Thành phần Máy khách được tải chậm, Next.js cũng sẽ gửi một khối tải trọng RSC chứa các hướng dẫn về cách tìm nạp và tải mã của thành phần được tải chậm. Điều này thể hiện cải thiện hiệu suất đáng kể vì quá trình tải trang không bị JavaScript kéo dài, thậm chí có thể không được tải trong phiên đó.
Vào thời điểm tôi viết bài này, phương pháp động để tải chậm một Thành phần Máy khách trong một Thành phần Máy chủ trong Next.js không hoạt động như bạn mong đợi. Để tải chậm một Thành phần Máy khách một cách hiệu quả, hãy đặt nó vào Thành phần Máy khách “wrapper” sử dụng chính phương thức
dynamic
để tải chậm Thành phần Máy khách thực tế. Wrapper sẽ được chuyển thành một tập lệnh tìm nạp và tải các tệp JavaScript và CSS của Thành phần Máy khách vào thời điểm cần thiết.TL;DR
Tôi biết rằng có rất nhiều đĩa quay và các mảnh di chuyển xung quanh vào những thời điểm khác nhau. Tuy nhiên, tóm lại là khi truy cập trang, Next.js sẽ hiển thị càng nhiều HTML càng tốt, sử dụng các giá trị dự phòng cho bất kỳ thành phần nào bị treo, sau đó gửi nội dung đó đến trình duyệt. Trong khi đó, Next.js kích hoạt các thành phần bất đồng bộ bị treo và định dạng chúng trong HTML và chứa trong RSC Payload được truyền phát đến trình duyệt, từng cái một, cùng với một tập lệnh$RC
biết cách hoán đổi các thứ.Dòng thời gian tải trang
Bây giờ, chúng ta hẳn đã hiểu rõ về cách RSC hoạt động, cách Next.js xử lý quá trình hiển thị của chúng và cách tất cả các thành phần khớp với nhau. Trong phần này, chúng ta sẽ phóng to những gì thực sự xảy ra khi chúng ta truy cập trang RSC trong trình duyệt.Tải ban đầu
Như đã đề cập trong phần TL;DR ở trên, khi truy cập một trang, Next.js sẽ hiển thị HTML ban đầu trừ đi thành phần bị treo và truyền phát đến trình duyệt như một phần của các khối truyền phát đầu tiên.Để xem mọi thứ xảy ra trong quá trình tải trang, chúng ta sẽ truy cập tab "Hiệu suất" trong Chrome DevTools và nhấp vào nút "tải lại" để tải lại trang và ghi lại hồ sơ. Đây là giao diện của nó:

Khi chúng ta phóng to ở phần đầu, chúng ta có thể thấy khoảng "Parse HTML" đầu tiên. Đó là máy chủ đang truyền phát các phần đầu tiên của tài liệu đến trình duyệt. Trình duyệt vừa nhận được HTML ban đầu, chứa shell trang và một vài liên kết đến các tài nguyên như phông chữ, tệp CSS và JavaScript. Trình duyệt bắt đầu gọi các tập lệnh.

Sau một thời gian, chúng ta bắt đầu thấy các khung hình đầu tiên của trang xuất hiện, cùng với các tập lệnh JavaScript ban đầu được tải và quá trình hydrat hóa diễn ra. Nếu bạn nhìn kỹ vào khung hình, bạn sẽ thấy toàn bộ vỏ trang được hiển thị và các thành phần "đang tải" được sử dụng tại nơi có các Thành phần Máy chủ bị treo. Bạn có thể nhận thấy rằng điều này diễn ra trong khoảng 800ms, trong khi trình duyệt bắt đầu nhận HTML đầu tiên ở 100ms. Trong 700ms đó, trình duyệt liên tục nhận các đoạn từ máy chủ.
Hãy nhớ rằng đây là ứng dụng demo Next.js chạy cục bộ ở chế độ phát triển, vì vậy nó sẽ chậm hơn so với khi chạy ở chế độ sản xuất.
Thành phần bị treo
Chuyển tiếp vài giây và chúng ta thấy một khoảng "Phân tích cú pháp HTML" khác trong dòng thời gian tải trang, nhưng khoảng này cho biết rằng Thành phần máy chủ bị treo đã tải xong và đang được truyền phát đến trình duyệt.
Chúng ta cũng có thể thấy rằng một Thành phần Khách hàng được tải chậm được phát hiện cùng lúc và nó chứa các tệp CSS và JavaScript cần được truy xuất. Các tệp này không phải là một phần của gói ban đầu vì thành phần không cần thiết cho đến sau này; mã được chia thành các tệp riêng của chúng.
Cách chia tách mã này chắc chắn cải thiện hiệu suất tải trang ban đầu. Nó cũng đảm bảo rằng mã của Thành phần Máy khách chỉ được chuyển đi nếu cần. Nếu Thành phần Máy chủ (hoạt động như thành phần cha của Thành phần Máy khách) trả về lỗi, thì Thành phần Máy khách sẽ không tải. Không hợp lý khi tải toàn bộ mã của nó trước khi chúng ta biết liệu nó có tải được hay không.
Hình 12 cho thấy sự kiện
DOMContentLoaded
được báo cáo vào cuối dòng thời gian tải trang. Và ngay trước đó, chúng ta có thể thấy rằng yêu cầu HTTP localhost
kết thúc. Điều đó có nghĩa là máy chủ có thể đã gửi khối có kích thước bằng không cuối cùng, cho biết với máy khách rằng dữ liệu đã được truyền hoàn toàn và có thể đóng giao tiếp phát trực tuyến.Kết quả cuối cùng
Yêu cầu HTTPlocalhost
chính mất khoảng năm giây, nhưng nhờ phát trực tuyến, chúng tôi bắt đầu thấy nội dung trang tải sớm hơn nhiều so với thời gian đó. Nếu đây là thiết lập SSR truyền thống, chúng tôi có thể sẽ phải nhìn chằm chằm vào màn hình trống trong năm giây đó trước khi bất kỳ thứ gì xuất hiện. Mặt khác, nếu đây là thiết lập CSR truyền thống, chúng tôi có thể sẽ phải chuyển nhiều JavaScript hơn và gây gánh nặng cho cả trình duyệt và mạng.Tuy nhiên, theo cách này, ứng dụng hoàn toàn tương tác trong năm giây đó. Chúng tôi có thể điều hướng giữa các trang và tương tác với các Thành phần máy khách đã được tải như một phần của gói chính ban đầu. Đây là một chiến thắng thuần túy theo quan điểm trải nghiệm người dùng.
Kết luận
RSC đánh dấu sự phát triển đáng kể trong hệ sinh thái React. Họ tận dụng thế mạnh của việc kết xuất phía máy chủ và phía máy khách trong khi áp dụng luồng HTML để tăng tốc độ phân phối nội dung. Cách tiếp cận này không chỉ giải quyết các vấn đề về SEO và thời gian tải mà chúng tôi gặp phải với CSR mà còn cải thiện SSR bằng cách giảm tải máy chủ, do đó nâng cao hiệu suất.Tôi đã cấu trúc lại cùng một ứng dụng RSC mà tôi đã chia sẻ trước đó để nó sử dụng bộ định tuyến Trang Next.js với SSR. Những cải tiến trong RSC rất đáng kể:

Khi xem hai báo cáo này mà tôi đã lấy từ Sentry, chúng ta có thể thấy rằng phát trực tuyến cho phép trang bắt đầu tải tài nguyên của nó trước khi yêu cầu thực tế hoàn tất. Điều này cải thiện đáng kể số liệu Web Vitals mà chúng ta thấy khi so sánh hai báo cáo.
Kết luận: Người dùng tận hưởng giao diện nhanh hơn, phản ứng tốt hơn với kiến trúc dựa trên RSC.
Kiến trúc RSC giới thiệu hai loại thành phần mới: Thành phần máy chủ và Thành phần máy khách. Sự phân chia này giúp React và các khuôn khổ dựa trên nó — như Next.js — hợp lý hóa việc phân phối nội dung trong khi vẫn duy trì tính tương tác.
Tuy nhiên, thiết lập này cũng đưa ra những thách thức mới trong các lĩnh vực như quản lý trạng thái, xác thực và kiến trúc thành phần. Khám phá những thách thức đó là một chủ đề tuyệt vời cho một bài đăng trên blog khác!
Bất chấp những thách thức này, những lợi ích của RSC là một trường hợp thuyết phục để áp dụng chúng. Chúng ta chắc chắn sẽ thấy các hướng dẫn được xuất bản về cách giải quyết các thách thức của RSC khi chúng hoàn thiện, nhưng theo tôi, chúng đã giống như tương lai của các hoạt động kết xuất trong phát triển web hiện đại.