精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

我們一起理解 React 服務端組件

開發 前端
簡單來說,內置 Suspense 組件能夠利用 Streaming SSR + React 服務端組件架構實現局部組件更新。這樣每塊內容都可以單獨渲染、處理,能更快響應用戶,帶來更好地瀏覽體驗。

有件事讓我感覺自己真的老了:React 今年已經 10 歲了。

自從 React 首次被引入以來,經歷了幾次演變。 React 團隊并不羞于改變:如果他們發現了更好的問題解決方案,就會采用。

React 團隊推出了 React 服務端組件(React  Server Components),這是最新的編寫范式。 React 組件有史以來第一次可以專門在服務器上運行。

網上對這個概念有太多不理解。許多人對服務端組件是什么、如何工作、有什么好處以及是如何與服務器端渲染等內容結合使用存在很多疑問。

我一直在使用 React 服務端組件進行大量實驗,也回答了我自己產生的很多問題。我必須承認,我對這些東西比我預想的要興奮得多,因為它真的很酷!

今天,我將幫助你揭開 React 服務端組件的神秘面紗,回答你可能對 React 服務端組件存在的許多問題!

服務端渲染快速入門

由于實際場景中,React 服務端組件通常與服務端渲染(Server Side Rendering,簡稱 SSR)配合使用,因此預先了解服務端渲染的工作原理會很有幫助。當然,如果你已經很熟悉 SSR 了,則可以跳過本節的學習。

在我 2015 年第一次使用 React 時,那時候的大多數 React 項目都還采用“客戶端渲染”策略。

在客戶端渲染模式下,用戶會先收到下面這樣一個比較簡單的網頁。

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>
    <script src="/static/js/bundle.js"></script>
  </body>
</html>

bundle.js 包含整個項目初始化和運行階段的所有代碼。包括 React、其他三方依賴以及我們自己的業務代碼。

JS 文件下載并解析后,React 會立即介入,準備好渲染應用所需要的 DOM 節點,并插入到空的 <div id="root"> 里。到這里,用戶就得到可以交互的頁面了。

雖然這個空的 HTML 文檔會很快接收,但 JS 文件的下載和解析是需要一些時間的,另外隨著我們項目規模的擴大,JS 文件本身的體積可能也在不斷變大。

在客戶端接收到 HTML 文檔,到 JS 文件處理結束的中間階段,用戶通常會面臨白屏問題,這種體驗就比較糟糕了。

服務端渲染就能有效的避免這種體驗。服務端渲染會將我們首屏要展示的 HTML 內容在服務端預先生成,再發送到客戶端。這樣,客戶端在接收到 HTML 時,就能渲染首屏內容,也就不會遇到白屏問題了。

當然,服務端渲染的 HTML 網頁同樣會包含 <script> 標簽,因為發送的首屏內容還需要交由 React 托管,附加交互能力。具體來說:與客戶端從頭構建 DOM 不同,服務端渲染模式下,React 會利用現有的 HTML 結構進行構建,并為 DOM 節點附加交互能力,以便響應用戶操作。這個過程被稱為“水合(hydration)”。

我很喜歡 React 核心團隊成員 Dan Abramov 對這一過程的通俗解釋:

水合(Hydration)就類似使用交互和事件處理程序的“水”澆到“干”的 HTML 上。

JS 包下載后,React 將快速運行我們的整個應用程序,構建 UI 的虛擬草圖,并將其“擬合”到真實的 DOM 節點、附加事件處理程序、觸發 effect 等。

簡而言之,SSR 就是服務器生成初始 HTML,這樣用戶在等待 JS 處理過程中,不會看到白屏。另外,客戶端 React 會接手服務器端 React 的工作,為 DOM 加入交互能力。

?? 關于靜態站點生成

當我們談論服務器端渲染時,我們通常想到的可能是下面的流程:

  1. 用戶訪問 myWebsite.com
  2. Node.js 服務器接收請求,并立即渲染 React 應用程序,生成 HTML
  3. 服務端生成的 HTML 被發送到客戶端

這是實現服務器端渲染的一種可能方法,但不是唯一的方法。另一種選擇是在構建(build)應用程序時生成 HTML。

通常,React 應用程序需要進行編譯,將 JSX 轉換為普通的 JavaScript,并打包我們的所有模塊。如果在這一過程中,我們為所有不同的路由“預渲染”所有 HTML 如何?

這種做法通常稱為靜態站點生成 (static site generatio,簡稱 SSG),它是服務器端渲染的一個變體。

在我看來,“服務器端渲染”是一個通用術語,包括幾種不同的渲染策略。不過,都有一個共同點:初始渲染都是使用 ReactDOMServer API,發生在 Node.js 等服務器運行時環境。

現有渲染方案分析

本節我們再來談談 React 中的數據獲取。通常,我們有兩個通過網絡進行通信的獨立應用程序:

  • 客戶端 React 應用程序
  • 服務器端 REST API

在客戶端我們使用類似 React Query、SWR 或 Apollo 這樣的工具向后端發起網絡請求,從后端數據庫中獲取數據并通過網絡發送回來。

我們可以將這一過程可視化成下面這樣。

圖片圖片

這里就展示了客戶端渲染 (CSR) 的工作流程。從客戶端接收到 HTML 開始。這個 HTML 文檔不包含任何內容,但會有一個或多個 <script> 標簽。

JS 文件下載并解析好后,React 應用程序將啟動,創建一堆 DOM 節點并填充 UI。不過,一開始我們沒有任何實際數據,因此往往會使用一個骨架屏來表示處于加載狀態中,這一階段稱為“Render Shell”,也就是“渲染骨架屏”。

這種模式很常見了。以 UberEats 網站舉例,在獲取到實際數據前,會展示下面的加載效果。

圖片圖片

在獲取實際數據并替換當前內容前,用戶會一直看到這個加載頁面。

以上就是典型的客戶端渲染方案。再來看看服務端渲染方案的執行流程。

圖片圖片

可以看到,“Render Shell”階段被放在了服務端,也就是說用戶收到就不是空白 HTML 了,這是比客戶端渲染好一點的地方,至少沒有白屏了。

為了方便比較,我們在圖標中有增加了一些常用網絡性能指標。看看在這兩個流程之間切換,有哪些指標發生了改變。

圖片圖片

圖表中這些 Web 性能指標的介紹如下:

  1. First Paint(首次繪制):因為總體布局在服務端渲染了,所以用戶不會看到白屏了。這個指標還叫 First Contentful Paint,即首次內容繪制,簡稱 FCP
  2. Page Interactive:React 下載好了,應用也經過渲染、水合處理了,現在頁面元素能夠響應交互了。這個指標還叫 Time To Interactive,即可交互時間,簡稱 TTI
  3. Content Paint:用戶想看的內容在頁面中出現了。也也就說我們從數據庫中拿到的數據在頁面中成功渲染了。這個指標還叫 Largest Contentful Paint,即最大內容繪制,簡稱 LCP

通過在服務器上進行初始渲染,我們能夠更快地繪制初始“Shell”頁面,即“骨架屏”頁面。體驗上會感覺更快一些,因為它提供了一種響應標識,告訴你頁面正在渲染。

某些情況下,這將是一個有意義的改進。但這樣的流程會感覺有點傻,用戶訪問我們的應用程序不是為了查看加載屏幕,而是為了查看內容。

當再次查看 SSR 圖時,我不禁想到如果把數據庫請求也放在服務器上執行,那么我們不就可以避免客戶端網頁的網絡請求了嗎?

換句話說,也就是下面這樣。

圖片圖片

我們不會在客戶端和服務器之間來回切換,當數據庫查詢結果作為初始請求的一部分時,在客戶端接收到的 HTML 文檔中,就包含用戶向看到的內容了。

不過,我們該怎么做呢?

React 并沒有提供這方面渲染方案的支持,不過生態系統針對這個問題提出了很多解決方案。像 Next.js 和 Gatsby 這樣的元框架(Meta Frameworks)就創造了自己的方式來專門在服務器上運行代碼。

以 Next.js 為例(使用舊的 Pages Router 模式):

import db from 'imaginary-db';
// This code only runs on the server:
export async function getServerSideProps() {
  const link = db.connect('localhost', 'root', 'passw0rd');
  const data = await db.query(link, 'SELECT * FROM products');
  return {
    props: { data },
  };
}
// This code runs on the server + on the client
export default function Homepage({ data }) {
  return (
    <>
      <h1>Trending Products</h1>
      {data.map((item) => (
        <article key={item.id}>
          <h2>{item.title}</h2>
          <p>{item.description}</p>
        </article>
      ))}
    </>
  );
}

這里簡單介紹下:當服務器收到請求時,會先調用 getServerSideProps 函數,它返回一個 props 對象。接著,這些 props 被傳給組件,這個組件會先使用這些 props 在服務器上進行一次渲染,然后將結果發送到客戶端,最后在客戶端進行水合。

getServerSideProps 是一個特殊的函數,只在服務器端執行,函數本身也不會包含在發送給客戶端的 JavaScript 文件中。

這種方法在當時是非常超前的,但也有一些缺點:

  1. 這個策略僅適用于路由級別的組件,也就是在整個頁面組件樹的最頂部的這個組件,而對后代子組件無法適用
  2. 這個策略并沒有標準化,導致每個元框架的具體實現各不相同。Next.js 是一種,Gatsby 則是另一種,Remix 再是一種
  3. 所有的 React 組件都會在客戶端上進行一次水合,即便組件本身可能并不需要(比如:沒有任何交互功功能、只是用于純展示作用的組件)

當然,React 團隊也意識到了這個問題,并一直嘗試給出一個官方方案。最終,方案確定了下來,也就是我們看到的 React Server Components,即 React 服務端組件,簡稱 RSC。

React 服務端組件介紹

React 服務端組件是一個全新的渲染模式,在這個模式下,組件完全在服務器上運行,讓我們可以組件中做類似查詢數據庫的后端操作。

下面是一個“服務端組件”的簡單示例。

import db from 'imaginary-db';
async function Homepage() {
  const link = db.connect('localhost', 'root', 'passw0rd');
  const data = await db.query(link, 'SELECT * FROM products');
  return (
    <>
      <h1>Trending Products</h1>
      {data.map((item) => (
        <article key={item.id}>
          <h2>{item.title}</h2>
          <p>{item.description}</p>
        </article>
      ))}
    </>
  );
}
export default Homepage;

如果你已經寫了很多年的 React,這樣的代碼一定會讓你感覺奇怪 ??。

我就是其中之一。當我看到這種寫法時,本能地驚嘆道。 “函數組件不能異步呀!而且我們不能直接在渲染中出現這樣的副作用!”

這里要理解的關鍵點是:服務端組件只會渲染一次,永遠不會重新渲染。它們在服務器上運行一次生成 UI,并將渲染的值發送到客戶端并原地鎖定,輸出永遠不會改變。

這表示 React 的 API 的很大一部分與服務端組件是不兼容的。例如,我們不能使用 useSate(),因為狀態可以改變,但服務端組件不支持重新渲染。我們不能使用 useEffect(),因為它只在渲染后在客戶端上運行,而服務端組件是不會發送到客戶端的。

不過,由于服務端環境限制,也給服務端組件的編寫帶來一定靈活性。例如:在傳統客戶端 React 中,我們需要將副作用放入 useEffect() 回調或事件處理程序中,避免每次渲染時重復調用。但如果組件本身只運行一次,我們就不必擔心這個問題了!

服務端組件本身非常簡單,但“React 服務端組件”模式要復雜得多。這是因為我們還要支持以前的常規組件,混用就會帶來混亂。

為了與新的“React 服務端組件”做區分,傳統 React 組件被稱為“客戶端組件(Client Component)”。老實說,我不是很喜歡這個名字。

“客戶端組件”聽起來好像這些組件只在客戶端上渲染,實際上并非如此——客戶端組件在客戶端和服務器端都會渲染。

圖片圖片

我知道所有這些術語都非常令人困惑,所以我做了一下總結:

  • React 服務端組件(React Server Components)是這個新模式的名稱
  • 我們所了解的“標準”React 組件被重新命名為客戶端組件(Client Component),這是對舊事物的一個新稱呼
  • 這個新模式引入了一個新的類型組件:服務端組件(Server Component),這些組件專門在服務器上渲染,其代碼也不會包含在發送給客戶端的 JS Bundle 中,因此也不會參與水合或重新渲染

?? 服務端組件與服務器端渲染

這里必須要澄清一下:React 服務端組件并不是服務器端渲染的替代品。你不應該把 React Server Components 理解成“SSR 的 2.0 版本”

這 2 者更像是可以拼湊在一起的拼圖,相輔相成。

我們仍然需要服務器端渲染來生成初始 HTML。React Server Components 則是建立在基礎之上,讓我們從客戶端 JavaScript 包中省略這些組件,確保它們只在服務器上運行。

事實上,你也可以在沒有服務器端渲染的情況下使用 React 服務端組件。實踐中它們通常一起使用,來得到更好的結果。如果你想查看示例,React 團隊已經構建了一個沒有 SSR 的最小 RSC demo[2]。

在使用服務端組件之前

通常,當新的 React 功能出現時,我們可以通過將 React 依賴項升級到最新版本來使用,類似 npm install react@latest 就可以了,不過服務端組件不是這樣。

我的理解是:服務端組件需要與 React 之外的一些系統緊密配合才能使用,比如打包工具(bundler)、服務器、路由之類的。

當我寫這篇文章時,Next.js 13.4+ 通過引入全新的重新架構“App Router” 來支持服務端組件的使用。

當然,在可以遇見的將來,會有越來越多的基于 React 的框架會支持這一特性。React 官方文檔有一個 “Bleeding-edge frameworks”[3] 的部分,其中列出了支持 React 服務端組件的框架列表。

使用客戶端組件

在 Next.js App Router 架構下,默認所有組件都會被看作服務端組件,客戶端組件需要特別聲明,這需要通過一個新的指令說明。

'use client';

import React from 'react';

function Counter() {
  const [count, setCount] = React.useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Current value: {count}
    </button>
  );
}
export default Counter;

注意,這里頂部的 'use client',這就是在告訴 React 這是一個客戶端組件,應該包含在 JS Bundle 中,以便在客戶端上重新渲染。

這種聲明方式借鑒了 JavaScript 的嚴格模式聲明——'use strict'。

在 App Router 架構下,所有組件默認被看作是服務端組件,無需任何聲明。當然,你可能會想到服務端組件是不是使用 'use server'——NO,不是!'use server' 其實是用在 Server Actions,而非服務端組件上的,不過這塊內容超出了本文范圍就不講了,有興趣的同學可以私下學習。

?? 哪些組件應該是客戶端組件?

這里你可能就有疑問了:我該怎么知道一個組件應該是服務端組件還是客戶端組件呢?

這里可以給大家一個一般規則:如果一個組件可以是服務端組件,那么它就應該是服務端組件。服務端組件往往更簡單且更容易推理,還有一個性能優勢,即服務端組件不在客戶端上運行,所以它們的代碼不包含在我們的 JavaScript 包中。因此,React 服務端組件對改進頁面交互指標(TTI)有所幫助。

不過,這不意味著我們要盡可能把作為組件都改成服務端組件,不合理也不可能。在 RSC 之前,每個 React 應用程序中的 React 組件都是客戶端組件。

當你開始使用 React 服務端組件時,你會發現它寫起來這非常直觀。而我們的一些組件由于需要狀態或 Effect,只能在客戶端上運行。你可以通過在組件頂部添加 'use client' 指令指定當前組件是客戶端組件,否則默認就是服務端組件。

客戶端邊界

當我熟悉 React 服務端組件時,我遇到的第一個問題是:如果組建 props 改變了,會發生什么?

假設,我們有一個像這樣的服務端組件:

function HitCounter({ hits }) {
  return (
    <div>
      Number of hits: {hits}
    </div>
  );
}

如果在初始服務器端渲染中, hits 等于 0 。然后,這個組件將生成以下結果。

<div>
  Number of hits: 0
</div>

但是,如果 hits 的值發生變化會怎樣?假設它是一個狀態變量,從 0 更成了 1。HitCounter 這個時候就需要重新渲染,但它不能重新渲染,因為它是服務端組件!

這里的問題是,如果沒有上下文環境,只是孤立的考慮服務端組件并沒有真正的意義。我們必須擴大范圍,從更高的角度審視,考慮我們應用程序的結構。

假設我們有如下的組件樹結構:

圖片圖片

如果所有這些組件都是服務端組件,那么就不會存在上面的問題,因為所有組件都不會重新渲染,props 也就沒有改變的可能性。

但假設 Article 組件擁有 hits 狀態變量。為了使用狀態,我們需要將其轉換為客戶端組件:

圖片圖片

你觀察到這里的問題了嗎?當 Article 重新渲染時,任何下屬子組件也會重新渲染,包括 HitCounter 和 Discussion。但是,如果這些是服務端組件,是無法重新渲染的。

為了避免這類矛盾場景的出現,React 團隊添加了一條規則:客戶端組件只能導入其他客戶端組件。'use client' 指令表示 HitCounter 和 Discussion 的這些實例將自動成為客戶端組件。

我在使用 React 服務端組件時遇到的最大的“啊哈(ah-ha)”時刻之一,是意識到服務端組件的這種新模式其實就是關于創建客戶端邊界的(client boundaries)。在實踐中,總會遇到下面的場景:

圖片圖片

當我們將 'use client' 指令添加到 Article 組件時,我們創建了一個“客戶端邊界”。邊界內的所有組件都隱式成為客戶端組件。即使像 HitCounter 這樣的組件沒有使用 'use client' 指令,在這種特殊情況下它們仍然會在客戶端上進行水合和渲染。

也就是說,我們不必將 'use client' 添加到每個客戶端上運行的組件,只需要在創建新的客戶端邊界的組件上添加即可。

解決服務端組件帶來的限制問題

當我第一次了解到客戶端組件無法渲染服務端組件時,它對我來說感覺非常限制。如果我需要在應用程序中使用高層狀態怎么辦?那所有組件豈不是都成為客戶端組件了?

事實證明,在許多情況下,我們可以通過重構組件來解決這個限制。

這是一件很難解釋的事情,所以讓我們先舉個例子說明:

'use client';

import { DARK_COLORS, LIGHT_COLORS } from '@/constants.js';

import Header from './Header';
import MainContent from './MainContent';

function Homepage() {
  const [colorTheme, setColorTheme] = React.useState('light');
  const colorVariables = colorTheme === 'light'
    ? LIGHT_COLORS
    : DARK_COLORS;
  return (
    <body style={colorVariables}>
      <Header />
      <MainContent />
    </body>
  );
}

在這段代碼中,我們需要使用 React 狀態允許用戶在深色/淺色模式之間切換。這類功能通常需要在應用程序樹的較高層級設置,以便我們可以將 CSS 變量 token 應用到 <body> 上。

為了使用狀態,我們需要讓 Homepage 成為客戶端組件。由于這是我們應用程序的頂部,表示其他所有組件 - Header 和 MainContent - 也將隱式成為客戶端組件。

為了解決這個問題,讓我們將主題管理提取到單獨的組件文件中:

// /components/ColorProvider.js
'use client';

import { DARK_COLORS, LIGHT_COLORS } from '@/constants.js';

function ColorProvider({ children }) {
  const [colorTheme, setColorTheme] = React.useState('light');
  const colorVariables = colorTheme === 'light'
    ? LIGHT_COLORS
    : DARK_COLORS;
  return (
    <body style={colorVariables}>
      {children}
    </body>
  );
}

返回 HomaPage,就可以像這樣重新組織了:

// /components/Homepage.js
import Header from './Header';
import MainContent from './MainContent';
import ColorProvider from './ColorProvider';

function Homepage() {
  return (
    <ColorProvider>
      <Header />
      <MainContent />
    </ColorProvider>
  );
}

現在就可以從 Homepage 中刪除 'use client' 指令了,因為它不再使用狀態或任何其他客戶端 React 功能,也就表示 Header 和 MainContent 不再需要被迫轉換成客戶端組件了!

當然,你可能會有疑問了。ColorProvider 是一個客戶端組件,是 Header 和 MainContent 的父組件。不管怎樣,它仍然處在樹結構的較高層級,是吧?

確實。不過,Header 和 MainContent 是在 Homepage 中引入的,這表示它們的 props 只受到 HomaPage 影響。也就是說,客戶端邊界只對邊界頂部組件的內部有影響,對同處于一個父組件下的其他組件沒有影響。

請記住,我們試圖解決的問題是服務端組件無法重新渲染的問題,因此無法為它們的任何子組件設置新的 props。Homepage 決定 Header 和 MainContent 的 props 是什么,并且由于 Homepage 本身是一個服務端組件,那么同屬于服務端組件的 Header、MainContent 自然就沒有 props 會改變的擔憂。

不得不承認的是,理解服務端組件架構確實是一件費腦筋的事情。即使有了多年的 React 經驗,我仍然覺得這很令人困惑,需要相當多的練習才能培養對這種新架構的直覺。

更準確地說,'use client' 指令是在文件/模塊級別下工作的。客戶端組件中導入的任何模塊也必須是客戶端組件。畢竟,當打包工具打包我們的代碼時,也是依據這些導入聲明一同打包的!

淺析底層實現

現在讓我們從一個較低的層面來看服務端組件的實現。當我們使用服務端組件時,輸出是什么樣的?實際生成了什么?

讓我們從一個超級簡單的 React 應用程序開始:

function Homepage() {
  return (
    <p>
      Hello world!
    </p>
  );
}

在 Next.js App Router 模式下,所有組件默認都是服務端組件。也就是說,Homepage 就是服務端組件,會在服務端渲染。

當我們在瀏覽器中訪問此應用程序時,我們將收到一個 HTML 文檔,如下所示:

<!DOCTYPE html>
<html>
  <body>
    <p>Hello world!</p>
    <script src="/static/js/bundle.js"></script>
    <script>
      self.__next['$Homepage-1'] = {
        type: 'p',
        props: null,
        children: "Hello world!",
      };
    </script>
  </body>
</html>

我們看到 HTML 文檔包含由 React 應用程序生成的 UI,即“Hello world!”段落。其實這屬于服務器端渲染結果,跟 React 服務端組件沒有關系。

再往下,是一個 <script> 標簽來加載我們的 JS 包。這個腳本中包括 React 等依賴項,以及我們應用程序中使用的所有客戶端組件代碼。由于我們的 Homepage 是服務端組件,所以這個組件的代碼不包含在這個 JS 包中。

最后,第二個 <script> 標簽,其中包含一些內聯 JS:

self.__next['$Homepage-1'] = {
  type: 'p',
  props: null,
  children: "Hello world!",
};

這里就比較有趣了。本質上這里所做的就是告訴 React——“嘿,我知道你看不到 Homepage 組件代碼,但不用擔心:這就是它渲染的內容”。通常來說,當 React 在客戶端上水合時,這種做法會加速整個渲染進程,因為部分組件(服務端組件)已經在后端渲染出來了,其組件代碼也不會包含在 JS 文件中。

我們會將服務器生成的虛擬表示發送回去,當 React 在客戶端加載時,它會重用這這部分虛擬描述,而不是重新生成它。

這就是上面的 ColorProvider 能夠工作的原因。 Header 和 MainContent 的輸出通過 children 屬性傳遞到 ColorProvider 組件。ColorProvider 可以根據需要重新渲染,但數據是靜態的,在服務器就鎖定了。

如果你想了解服務端組件如何序列化并通過網絡發送的,可以使用 Alvar Lagerl?f 開發的 RSC Devtools[4] 進行查看。

?? 服務端組件不需要服務器

我們有一道,服務器端渲染其實是很多不同渲染策略的總稱。包括:

  1. 靜態的:HTML 是在構建階段生成的
  2. 動態的:HTML 是在用戶請求是生成的,即“按需”生成的

React Server Components 與上述這 2 渲染策略都是兼容的。當服務端組件在 Node.js 調用渲染時,會返回的當前組件的 JavaScript 對象表示。這個操作可以在構建時,也可以在請求時。

也就是說,在沒有服務器的情況下使用 React 服務端組件!我們可以生成一堆靜態 HTML 文件并將它們托管在某個地方,事實上,這就是 Next.js App Router 中默認就是這個策略——除非我們真的需要推遲到“請求”階段,否則所有這些工作都會在構建期間提前發生。

服務端組件的好處

React 服務端組件比較酷的一點就在于:它是 React 中運行服務器專有代碼的第一個“官方”方案。另外,自 2016 年以來,我們已經能夠在 Next.js 的 App Router 模式下使用服務端組件了!

不過,這種方案引入之后,編寫 React 代碼的方式變得很不一樣了,因為我們需要編寫專用于服務端的 React 的代碼了。

這樣帶來的一個最明顯好處就是性能了。服務端組件不包含在我們發送給客戶端的 JS 包中,這樣就減少了需要下載的 JS 代碼數量以及需要水合的組件數量:

圖片圖片

不過,這對我來說可能是最不令人興奮的事情。畢竟,大多數 Next.js 應用程序在“頁面可交互(Page Interactive)”方面已經做得足夠快了。

如果你遵循語義 HTML 原則,那么你的大部分應用程序甚至在 React 水合之前就可以運行。比如:跳轉鏈接、提交表單、展開和折疊手風琴(使用 <details> 和 <summary>)等。者對于大多數項目來說,React 只需要幾秒鐘的時間來進行水合就很不錯了。

不過,React 服務端組件真正的優勢在于,我們不再需要在功能與打包文件尺寸上妥協了!

例如,大多數技術博客都需要某種語法高亮庫。在我的博客里,我使用 Prism。代碼片段如下所示:

function exampleJavaScriptFunction(param) {
  return "Hello world!"
}

一個流行語法高亮庫,通常會支持很多流行的編程語言,有幾兆字節,放到 JS 包中實在太大。因此,我們必須做出妥協,刪除非必須語言和功能。

但是,假設我們在服務端組件中進行語法突出顯示。在這種情況下,我們的 JS 包中實際上不會包含高亮庫代碼。因此,我們不必做出任何妥協,另外我們還可以使用所有的附加功能。

Bright[5] 就是支持在服務端組件中使用的現代語法高亮庫。

圖片圖片

這是讓我對 React 服務端感到興奮的一個地方。原本包含在 JS 包中成本太高的東西現在可以在服務器上運行,而不必在包含在 JS 包中了,這也帶來了更好的用戶體驗。

這也不僅僅是性能和用戶體驗。使用 RSC 一段時間后,我開始真正體會到服務端組件是多么簡單易用。我們永遠不必擔心依賴數組、過時的閉包、記憶或由事物變化引起的任何其他復雜的東西。

我真的很高興看到未來幾年事情將如何發展,因為社區將利用這種新模式繼續創造出像 Bright 這樣新的解決方案。對于成為一名 React 開發者來說,這很令人激動!

完整圖表

React 服務端組件是一項令人興奮的方案,但它實際上只是“現代 React”難題的一部分。

當我們將 React 服務端組件與 Suspense 和新的 Streaming SSR 架構結合起來時,事情變得更加有趣。它允許我們做下面這樣瘋狂的事情:

圖片圖片

簡單來說,內置 Suspense 組件能夠利用 Streaming SSR + React 服務端組件架構實現局部組件更新。這樣每塊內容都可以單獨渲染、處理,能更快響應用戶,帶來更好地瀏覽體驗。

不過這部分知識超出了本文范圍,你可以在 Github[6] 上了解有關此架構的更多信息。

參考資料

[1]

Making Sense of React Server Components: https://www.joshwcomeau.com/react/server-components/

[2]最小 RSC demo: https://github.com/reactjs/server-components-demo

[3]“Bleeding-edge frameworks”: https://react.dev/learn/start-a-new-react-project#bleeding-edge-react-frameworks

[4]RSC Devtools: https://www.alvar.dev/blog/creating-devtools-for-react-server-components

[5]Bright: https://bright.codehike.org/

[6]Github: https://github.com/reactwg/react-18/discussions/37

責任編輯:武曉燕 來源: 寫代碼的寶哥
相關推薦

2024-02-22 17:54:30

React編譯器團隊

2024-03-28 08:50:58

Flink分配方式后端

2023-09-26 00:53:37

B端搭建低代碼

2024-02-04 09:27:09

ReactNativeJS

2024-01-29 09:01:20

React列表模式

2024-06-07 14:54:55

2021-12-10 07:45:48

字節音頻視頻

2023-01-04 18:10:26

服務模塊化jre

2021-10-26 09:55:52

CAP理論分布式

2022-08-29 07:48:27

文件數據參數類型

2025-03-17 11:21:08

APISwagger界面

2022-03-08 17:52:58

TCP格式IP

2024-02-20 21:34:16

循環GolangGo

2021-08-27 07:06:10

IOJava抽象

2022-01-17 06:59:40

Grep指令linux

2021-07-28 07:53:20

Github ActiDotnet 應用

2021-12-29 08:27:05

ByteBuffer磁盤服務器

2022-03-31 18:59:43

數據庫InnoDBMySQL

2023-06-30 08:18:51

敏捷開發模式

2022-05-24 08:21:16

數據安全API
點贊
收藏

51CTO技術棧公眾號

精品中文字幕一区| 欧美精品videosex牲欧美| 最近免费中文字幕中文高清百度| 欧美日韩国产综合视频| 蜜臀av性久久久久蜜臀aⅴ| 日韩视频免费观看| 国产精品一区二区人妻喷水| 色8久久影院午夜场| 亚洲精品视频在线观看免费| 成人av免费在线看| 国产字幕在线观看| 国产精品chinese| 亚洲人成电影网站| 亚洲av综合色区无码另类小说| 欧美aa一级| 亚洲三级在线免费观看| 久久综合福利| 国产激情无套内精对白视频| 欧美专区一区二区三区| 久久成年人视频| 成人片黄网站色大片免费毛片| 在线播放成人| 91久久一区二区| 久久这里只有精品8| 国产精品四虎| 99热99精品| 99re在线| 国产美女主播在线观看| 日韩精品电影在线| 6080yy精品一区二区三区| 手机在线免费看片| av亚洲在线观看| 亚洲国产精品大全| 国产精品久久久久久久99| 日本美女一区| 欧美丝袜美女中出在线| 日产精品久久久久久久蜜臀| 色网站在线看| 欧美国产日韩精品免费观看| 久久人人97超碰人人澡爱香蕉| 国产成人麻豆精品午夜在线| 久久精品国产网站| 国产精品久久久久久久久借妻| 国产精品18p| 欧美人成网站| 欧美精品做受xxx性少妇| 情侣偷拍对白清晰饥渴难耐| 欧美一区二区麻豆红桃视频| 亚洲欧美视频在线| 日本一区二区三区网站| 国内精品偷拍| 欧美一级片在线| 中文字幕一区二区在线观看视频| 国产精品原创视频| 欧美午夜精品一区二区三区| 国产女女做受ⅹxx高潮| av在线视屏| 午夜日韩在线电影| 久久av综合网| 国产在线精彩视频| 欧美日韩久久久久| 国产淫片av片久久久久久| 中文在线免费视频| 色丁香久综合在线久综合在线观看| 热99这里只有精品| 亚洲欧洲自拍| 欧美在线观看禁18| 性生活免费在线观看| 九色成人搞黄网站| 欧美日韩视频专区在线播放| 欧美三级午夜理伦三级富婆| 欧美日韩va| 欧美一区三区二区| 大尺度在线观看| 久久综合另类图片小说| 亚洲欧美精品在线| 午夜国产福利视频| 欧美日韩三级| 情事1991在线| 一级全黄少妇性色生活片| 国产原创一区二区三区| 草莓视频一区| 三级视频在线| 中文字幕av一区二区三区| 在线不卡日本| 免费不卡av| 日韩欧美在线视频| 911福利视频| 99re6热只有精品免费观看| 日韩黄色av网站| 无码人中文字幕| 国产精品v日韩精品v欧美精品网站 | 成人网av.com/| 日韩一级二级三级精品视频| 久久久久亚洲AV成人无码国产| 免费久久精品| 毛片精品免费在线观看| 国产精品黄色大片| 久国产精品韩国三级视频| 国产一级精品aaaaa看| 国产高清视频在线观看| 亚洲精品视频在线观看免费 | 天天鲁一鲁摸一摸爽一爽| 伊人久久大香线蕉综合热线 | 欧美高清hd| 亚洲女人初尝黑人巨大| 欧美一区二区三区爽爽爽| 国产毛片久久| 99se婷婷在线视频观看| 番号在线播放| 午夜精品福利一区二区三区av | 91美女视频网站| av磁力番号网| 欧洲成人一区| 日韩国产欧美精品一区二区三区| 国产一区在线观看免费| 国产欧美午夜| 成人av资源网| 欧美成人高清在线| 色悠悠久久综合| av av在线| 中文字幕一区二区三区欧美日韩| 国产精品aaa| 婷婷色在线观看| 亚洲另类色综合网站| 国产无套粉嫩白浆内谢的出处| 91蜜桃臀久久一区二区| 久久精品国产亚洲一区二区| 久久久久久无码午夜精品直播| 成人免费视频一区二区| 中文字幕一区二区三区最新| 桃色一区二区| 亚洲精品狠狠操| 久久久久久免费观看| 精品写真视频在线观看| 色综合视频二区偷拍在线| 午夜不卡影院| 亚洲精品国产综合区久久久久久久| 免费中文字幕在线观看| 国产精品18久久久久久久久 | 国产又粗又长又硬| 日日夜夜精品视频天天综合网| 狠狠色伊人亚洲综合网站色| 丰满大乳少妇在线观看网站| 欧美videossexotv100| 一区二区成人免费视频| 国产在线不卡一区| 久久久国产精华液999999| 欧美亚洲综合视频| 综合网中文字幕| 中文在线资源天堂| 中文字幕免费不卡| 亚洲一级片网站| 99视频精品全部免费在线视频| 国产精品中文字幕久久久| yw视频在线观看| 欧美日韩亚洲综合一区二区三区| jizz18女人高潮| 精品在线免费观看| 无码人妻精品一区二区三区99v| 日本免费精品| 国内偷自视频区视频综合| 俄罗斯嫩小性bbwbbw| 午夜久久久久久电影| 免费观看一级一片| 天堂成人国产精品一区| 亚洲国产欧美不卡在线观看| 四虎在线精品| 欧美精品福利在线| 日本黄在线观看| 欧美日韩三级一区二区| xxxx日本少妇| 不卡视频免费播放| 国模杨依粉嫩蝴蝶150p| 久久亚洲专区| 99re6热在线精品视频播放速度| 福利小视频在线| 亚洲欧美日韩精品久久亚洲区| 中文字幕第一页在线播放| 综合在线观看色| 又黄又爽的网站| 男女激情视频一区| 黄色一级片国产| 亚洲婷婷影院| 成人女保姆的销魂服务| av2020不卡| 深夜福利国产精品| 亚洲AV无码国产精品午夜字幕| 午夜国产精品影院在线观看| 国产又粗又硬视频| 国产91精品一区二区麻豆亚洲| av动漫在线观看| 天天做天天爱天天综合网2021| 国产精品久久久对白| 亚洲精品在线影院| 欧美激情精品久久久久久黑人| 毛片免费在线观看| 欧美一级二级三级蜜桃| 亚洲av中文无码乱人伦在线视色| 国产精品福利一区二区三区| 88av在线播放| 九九热在线视频观看这里只有精品| 日韩欧美不卡在线| 99久久亚洲精品| 久久精品中文字幕一区二区三区 | 91精品国产美女浴室洗澡无遮挡| 亚洲视频免费播放| 亚洲精品视频在线| 国产aaaaaaaaa| 91在线观看地址| wwwxxx色| 久久99精品久久久久久国产越南 | 国产成人精品亚洲日本在线桃色| 成人亚洲视频在线观看| 精品二区视频| 黄色网址在线免费看| 国产欧美高清视频在线| 国产精品视频500部| 亚洲精品777| 国产精品久久久久久久久久久新郎 | 4p变态网欧美系列| 中文在线观看免费| 日韩视频―中文字幕| 国产女主播在线写真| 亚洲精品xxx| 高清毛片aaaaaaaaa片| 7777精品伊人久久久大香线蕉最新版| 国产中文字幕视频| 五月婷婷久久综合| 久久久久久久久久综合| 亚洲人妖av一区二区| 男人舔女人下部高潮全视频| av成人免费在线观看| 日韩成人av影院| 国产成人一级电影| 激情久久综合网| 久久99精品一区二区三区三区| 久久久久久三级| 久久中文精品| 丁香啪啪综合成人亚洲| 亚洲一区二区三区高清| 日韩免费视频播放| 亚洲乱码久久| 成熟丰满熟妇高潮xxxxx视频| 亚洲国产第一| 欧美乱大交xxxxx潮喷l头像| 国模一区二区三区| 亚洲 欧美 综合 另类 中字| 欧美日韩视频一区二区三区| 成年人视频大全| 亚洲一级淫片| 久久综合久久久久| 影音先锋亚洲精品| 欧美日韩性生活片| 一区二区日韩免费看| 亚洲美免无码中文字幕在线| 亚洲少妇自拍| 99福利在线观看| 日韩精品91亚洲二区在线观看| 黄色片在线免费| 紧缚奴在线一区二区三区| 在线观看视频在线观看| 成人毛片老司机大片| 又黄又爽的网站| 久久久久久久久久久久久女国产乱 | 国产一区二区久久久久| 91网站免费观看| 澳门久久精品| 免费国产一区二区| 欧美韩日高清| 国产亚洲黄色片| 玖玖视频精品| √天堂资源在线| www.成人网.com| 国产综合精品久久久久成人av | 日韩在线视频免费播放| 色婷婷精品大在线视频| 91亚洲欧美激情| 精品久久久久99| 韩国福利在线| 久久中文精品视频| 中文一区一区三区高中清不卡免费| 国产精品九九九| jizz性欧美23| 亚洲精品欧洲精品| 亚洲小说区图片区| av在线无限看| 丰满少妇久久久久久久| 在线国产视频一区| 一区二区在线观看av| 中文字幕69页| 精品三级av在线| av电影在线观看| 高清欧美电影在线| 国产69精品久久久久9999人| 成人xxxxx色| 欧美大片aaaa| 国产人妻777人伦精品hd| 麻豆freexxxx性91精品| 漂亮人妻被黑人久久精品| 国产精品免费aⅴ片在线观看| 久久久久免费看| 欧美性生交片4| 天天摸天天干天天操| 久久精品国产视频| 欧美韩国亚洲| 国模精品娜娜一二三区| 婷婷亚洲五月| 激情婷婷综合网| 不卡一区中文字幕| 日韩一级片av| 欧美色网站导航| 色播色播色播色播色播在线| 美日韩丰满少妇在线观看| 偷拍中文亚洲欧美动漫| 国产精品一区二区三区精品| 久久影视一区| 亚洲视频在线a| 99re亚洲国产精品| 久久精品国产亚洲AV无码麻豆| 欧美日韩精品欧美日韩精品| 欧美日韩在线中文字幕| 久久久久久亚洲精品不卡| 成人豆花视频| 一区二区三区四区国产| 日韩中文字幕1| 中文字幕一区二区三区人妻| 亚洲成人在线免费| 亚洲男女视频在线观看| 欧美成人sm免费视频| 在线不卡一区| 男插女免费视频| 狠狠狠色丁香婷婷综合激情| eeuss中文字幕| 欧美视频一区在线观看| 国产精品影院在线| 国产激情久久久久| 国产一区二区三区探花| 日本黄网站免费| 久久亚洲精品小早川怜子| 日本中文字幕网| 日韩精品在线视频美女| 天堂av在线网| 欧美性xxxx69| 久久一区亚洲| 亚洲不卡的av| 欧美高清视频不卡网| 久久99精品久久| 91免费看网站| 伊人久久久大香线蕉综合直播| 秘密基地免费观看完整版中文| 亚洲一区二区在线观看视频| 国产77777| 91高清免费视频| 免费成人网www| www.夜夜爽| 一区二区在线观看av| 日韩一区二区三区不卡| 97视频在线看| 精品高清久久| 黄色小视频免费网站| 亚洲欧美二区三区| 免费观看黄色av| 91干在线观看| 残酷重口调教一区二区| 国产性生活一级片| 亚洲在线视频免费观看| 四虎影视在线播放| 国产精品久久久久久久久久久不卡 | 欧美aaaaa性bbbbb小妇| 欧洲亚洲一区| 精品一区二区三区免费观看| 久久久久久久福利| 日韩av影视在线| 成人黄色在线| 激情五月婷婷六月| 久久青草国产手机看片福利盒子| 国产精品露脸视频| 久久99久久99精品免观看粉嫩| 免费福利视频一区| 一级片视频免费观看| 一区二区激情视频| 日本人妖在线| 91免费在线观看网站| 久久国产精品99国产| 国产在线观看免费视频软件| 精品欧美一区二区三区精品久久| 久久sese| 国产成人生活片| 国产天堂亚洲国产碰碰| 国内老熟妇对白hdxxxx| 欧洲成人性视频| 欧美成人日韩| 性猛交娇小69hd| 亚洲精品在线观看视频| 91大神在线观看线路一区| 免费无码毛片一区二三区| **网站欧美大片在线观看| 亚洲 欧美 自拍偷拍| 成人有码视频在线播放|