目錄
Next.js
Redux Connect
react-frontload
理論
第一次渲染
第二次渲染
App
更改 index.html 以添加數據
服務
服務端渲染和客戶端渲染在 React 中有什麼區別?
如何在我的客戶端渲染的 React 應用程序中發出服務器端請求?
為什麼我的全局變量在 React 中執行兩次?
如何在服務端渲染的 React 中使用異步 API?
服務端渲染在 React 中有哪些好處?
如何在使用服務端渲染的 React 中的異步 API 時處理錯誤?
我可以在服務端渲染的 React 中使用鉤子嗎?
如何提高服務端渲染的 React 應用程序的性能?
如何測試我的服務端渲染的 React 應用程序?
我可以將服務端渲染與 Next.js 一起使用嗎?
首頁 web前端 js教程 處理服務器渲染的反應中的異步API

處理服務器渲染的反應中的異步API

Feb 16, 2025 am 11:52 AM

Dealing with Asynchronous APIs in Server-rendered React

要點總結

  • React 代碼的服務端渲染有助於縮短加載時間並提高 SEO 靈活性,但由於需要在知道所需數據之前渲染應用程序,因此處理異步 API 可能會面臨挑戰。
  • 現有的解決方案,例如 Next.js、Redux Connect 和 react-frontload,在處理服務端渲染的 React 代碼中的異步 API 時各有優缺點。
  • 可以通過執行兩次服務端渲染來實現自定義解決方案:第一次處理 API 調用和異步操作,第二次使用獲取的數據進行最終頁面渲染。
  • 自定義解決方案需要仔細處理組件中的不同狀態,包括預取、後取、預渲染和後端渲染。這可以通過組件代碼中的複雜 if 語句來實現。
  • 自定義解決方案還需要更改 index.html 文件,以便將預取數據作為頁面請求的一部分發送,並將其添加到搜索和替換中。如果使用腳本標籤,則需要進行 base-64 編碼。

如果您曾經製作過基本的 React 應用頁面,它可能會存在 SEO 差和性能問題,尤其是在較慢的設備上。您可以添加傳統的網頁服務端渲染(通常使用 NodeJS),但這並非一個簡單的過程,尤其是在處理異步 API 時。

服務端渲染代碼的兩個主要好處是:

  • 加快加載速度
  • 提高 SEO 靈活性

請記住,Google 會等待您的 JavaScript 加載,因此標題內容等簡單內容會無問題地更改。 (不過,我無法說明其他搜索引擎的情況,或者這有多可靠。)

在這篇文章中,我將討論在使用服務端渲染的 React 代碼時如何從異步 API 獲取數據。 React 代碼具有內置於 JavaScript 中的整個應用程序結構。這意味著,與具有控制器的傳統 MVC 模式不同,您在應用程序渲染之前不知道需要什麼數據。使用像 Create React App 這樣的框架,您可以快速創建高質量的工作應用程序,但它要求您僅在客戶端處理渲染。這存在性能問題,以及 SEO/數據問題,您可以在其中根據需要更改頭部。

問題

React 主要同步渲染,因此如果您沒有數據,則會渲染加載屏幕並等待數據到來。這在服務器端效果不佳,因為您在渲染之前不知道需要什麼,或者您知道需要什麼,但您已經渲染了。

查看此標準渲染方法:

ReactDOM.render(
  <provider> store={store}></provider>
    <browserrouter></browserrouter>
      <app></app>
    >
  >
, document.getElementById('root')
)
登入後複製
登入後複製
登入後複製
登入後複製

問題:

  1. 這是一個尋找根元素的 DOM 渲染。這在我的服務器上不存在,因此我們必須將其分開。
  2. 我們無法訪問主根元素之外的任何內容。我們無法設置 Facebook 標籤、標題、描述、各種 SEO 標籤,並且我們無法控制元素外部的其餘 DOM,尤其是頭部。
  3. 我們提供了一些狀態,但服務器和客戶端具有不同的狀態。我們需要考慮如何處理該狀態(在本例中為 Redux)。

因此,我在這裡使用了兩個庫,它們非常流行,因此希望它可以應用於您使用的其他庫。

Redux:存儲服務器和客戶端同步的狀態是一個噩夢般的問題。它非常昂貴,並且通常會導致複雜的錯誤。在服務器端,理想情況下,除了足以使事情正常工作並正確渲染之外,您不想使用 Redux 做任何事情。 (您仍然可以照常使用它;只需設置足夠的狀態使其看起來像客戶端。)如果您想嘗試,請查看各種分佈式系統指南作為起點。

React-Router:僅供參考,這是 v4 版本,這是默認安裝的版本,但如果您有較舊的現有項目,則會有很大不同。您需要確保在服務器端和客戶端處理路由,並且使用 v4——它在這方面非常出色。

畢竟,如果您需要進行數據庫調用怎麼辦?這突然成為一個大問題,因為它是非同步的,並且位於您的組件內部。當然,這不是一個新問題:在官方 React 存儲庫中查看它。

您必須進行渲染才能確定需要哪些依賴項——這些依賴項需要在運行時確定——並在提供給客戶端之前獲取這些依賴項。

現有解決方案

下面,我將回顧當前提供的用於解決此問題的解決方案。

Next.js

在我們開始之前,如果您想要生產環境的服務端渲染的 React 代碼或通用應用程序,Next.js 是您的理想選擇。它有效、簡潔,並且有 Zeit 支持。

但是,它是有主見的,您必須使用他們的工具鏈,並且他們處理異步數據加載的方式不一定那麼靈活。

查看 Next.js 存儲庫文檔中的這段直接複製內容:

ReactDOM.render(
  <provider> store={store}></provider>
    <browserrouter></browserrouter>
      <app></app>
    >
  >
, document.getElementById('root')
)
登入後複製
登入後複製
登入後複製
登入後複製

getInitialProps 是關鍵,它返回一個 promise,該 promise 解析為一個填充 props 的對象,並且僅在頁面上。最棒的是,這只是內置到他們的工具鏈中:添加它即可工作,無需任何工作!

那麼如何獲取數據庫數據呢?您進行 API 調用。您不想?好吧,太糟糕了。 (好的,您可以添加自定義內容,但您必須自己完全實現它。)但是,如果您考慮一下,這是一個非常合理且通常來說是良好的實踐,因為否則,您的客戶端仍然會進行相同的API 調用,並且服務器上的延遲幾乎可以忽略不計。

您還可以訪問的內容受到限制——幾乎只是請求對象;同樣,這似乎是良好的實踐,因為您無法訪問您的狀態,而您的狀態在服務器和客戶端上本來就不同。哦,如果您之前沒有註意到,它只適用於頂級頁面組件。

Redux Connect

Redux Connect 是一個非常有主見的服務器端渲染器,具有不錯的理念,但是如果您不使用他們描述的所有工具,這可能不適合您。此包有很多內容,但它非常複雜,尚未升級到 React Router v4。有很多設置,但讓我們來看最重要的部分,只是為了學習一些經驗教訓:

ReactDOM.render(
  <provider> store={store}></provider>
    <browserrouter></browserrouter>
      <app></app>
    >
  >
, document.getElementById('root')
)
登入後複製
登入後複製
登入後複製
登入後複製

裝飾器在 JavaScript 中不是標準的。在撰寫本文時,它們處於第 2 階段,因此請謹慎使用。這只是添加高階組件的另一種方式。這個想法很簡單:密鑰是傳遞給您的 props 的內容,然後您有一系列 promise,它們會解析並傳入。這看起來不錯。也許另一種選擇就是這個:

import React from 'react'
export default class extends React.Component {
  static async getInitialProps ({ req }) {
    return req
      ? { userAgent: req.headers['user-agent'] }
      : { userAgent: navigator.userAgent }
  }
  render () {
    return <div>
      Hello World {this.props.userAgent}
    </div>
  }
}
登入後複製
登入後複製

使用 JavaScript 可以做到這一點,而且不會出現太多問題。

react-frontload

react-frontload 存儲庫沒有很多文檔或解釋,但我所能獲得的最佳理解可能來自測試(例如這個測試)和閱讀源代碼。當某些內容被掛載時,它會被添加到 promise 隊列中,當該隊列解析時,它會被提供服務。它所做的事情非常好,儘管很難推荐一些沒有良好文檔、維護或使用的內容:

// 1. 连接您的数据,类似于 react-redux @connect
@asyncConnect([{
  key: 'lunch',
  promise: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' })
}])
class App extends React.Component {
  render() {
    // 2. 将数据作为 props 访问
    const lunch = this.props.lunch
    return (
      <div>{lunch.name}</div>
    )
  }
}
登入後複製
登入後複製

尋找更好的解決方案

以上解決方案都沒有真正符合我對庫的靈活性和簡單性的期望,因此我現在將介紹我自己的實現。目標不是編寫包,而是讓您了解如何根據您的用例編寫自己的包。

此示例解決方案的存儲庫位於此處。

理論

其背後的想法相對簡單,儘管最終會產生相當多的代碼。這是為了概述我們正在討論的想法。

服務器必須渲染 React 代碼兩次,我們只會為此使用 renderToString。我們希望在第一次和第二次渲染之間保持上下文。在我們的第一次渲染中,我們試圖消除任何 API 調用、promise 和異步操作。在我們的第二次渲染中,我們希望獲取我們獲得的所有數據並將其放回我們的上下文中,從而渲染我們的工作頁面以進行分發。這也意味著應用程序代碼需要根據上下文執行操作(或不執行操作),例如是否在服務器上或瀏覽器上,以及在任一情況下是否正在獲取數據。

此外,我們可以根據需要自定義它。在本例中,我們根據上下文更改狀態代碼和頭部。

第一次渲染

在您的代碼中,您需要知道您是在服務器上還是在瀏覽器上工作,理想情況下,您希望對它進行複雜控制。使用 React Router,您可以獲得一個靜態上下文 prop,這很棒,所以我們將使用它。目前,我們只是添加了一個數據對象和請求數據,正如我們從 Next.js中學到的那樣。我們的 API 在服務器和客戶端之間有所不同,因此您需要提供一個服務器 API,最好與您的客戶端 API 具有相似的接口:

ReactDOM.render(
  <provider> store={store}></provider>
    <browserrouter></browserrouter>
      <app></app>
    >
  >
, document.getElementById('root')
)
登入後複製
登入後複製
登入後複製
登入後複製

第二次渲染

在第一次渲染之後,我們將獲取那些掛起的 promise 並等待這些 promise 完成,然後重新渲染,更新上下文:

import React from 'react'
export default class extends React.Component {
  static async getInitialProps ({ req }) {
    return req
      ? { userAgent: req.headers['user-agent'] }
      : { userAgent: navigator.userAgent }
  }
  render () {
    return <div>
      Hello World {this.props.userAgent}
    </div>
  }
}
登入後複製
登入後複製

App

快速從我們的服務器跳轉到應用程序代碼:在我們任何具有路由器連接的組件中,我們現在都可以獲得它:

// 1. 连接您的数据,类似于 react-redux @connect
@asyncConnect([{
  key: 'lunch',
  promise: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' })
}])
class App extends React.Component {
  render() {
    // 2. 将数据作为 props 访问
    const lunch = this.props.lunch
    return (
      <div>{lunch.name}</div>
    )
  }
}
登入後複製
登入後複製

哇,這有很多複雜的代碼。在這個階段,您可能想要採用更具中繼的方法,在該方法中,您將數據獲取代碼分離到另一個組件中。

此組件由您可能熟悉的組件構成——渲染步驟和 componentWillMount 步驟。四階段 if 語句處理不同的狀態——預取、後取、預渲染和後端渲染。我們還在數據加載後添加到頭部。

最後,還有一個獲取數據步驟。理想情況下,您的 API 和數據庫具有相同的 API,這使得執行相同。您可能希望將這些放入 Thunk 或 Saga 中的操作中,以使其更具可擴展性。

查看文章“服務端 React 渲染”和存儲庫 React 服務端渲染以了解更多信息。請記住,您仍然需要處理數據未加載的狀態!您只會在第一次加載時進行服務器端渲染,因此您將在後續頁面上顯示加載屏幕。

更改 index.html 以添加數據

我們需要將任何預取數據作為頁面請求的一部分發送,因此我們將添加一個腳本標籤:

@asyncConnect([{
  lunch: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' })
}])
登入後複製

服務

然後我們需要將其添加到我們的搜索和替換中。但是,HTML 使用非常基本的腳本標籤查找器,因此如果您有腳本標籤,則需要對其進行 base-64 編碼。此外,不要忘記我們的頭部標籤!

const App = () => (
  <frontload>isServer</frontload>
    <component1> entityId='1' store={store}></component1>
  >
)

return frontloadServerRender(() => (
  render(<app></app>)
)).then((serverRenderedMarkup) => {
  console.log(serverRenderedMarkup)
})
登入後複製

我們還處理狀態代碼更改——例如,對於 404——因此,如果您有 404 頁面,您可以這樣做:

const context = {data: {}, head: [], req, api}
const store = configureStore()
renderToString(
  <provider> store={store}></provider>
    <staticrouter> location={req.url} context={context}>
      <app></app>
    >
  >
)
登入後複製

總結

如果您不確定自己在做什麼,只需使用 Next.js。它專為服務端渲染和通用應用程序而設計,或者如果您希望手動執行所有操作的靈活性,則可以按照您想要的方式進行。一個例子可能包括您在子組件中而不是在頁面級別進行數據獲取。

希望本文能幫助您入門!不要忘記查看 GitHub 存儲庫以獲取可行的實現。

關於異步 API 和服務端渲染 React 的常見問題解答 (FAQ)

服務端渲染和客戶端渲染在 React 中有什麼區別?

服務端渲染 (SSR) 和客戶端渲染 (CSR) 是渲染網頁的兩種不同方法。在 SSR 中,服務器會響應請求生成頁面的完整 HTML,然後將其發送到客戶端。這會導致更快的初始頁面加載時間,並且有利於 SEO。但是,這可能會導致頁面轉換速度變慢,因為每次請求都需要渲染整個頁面。另一方面,CSR 意味著渲染是在瀏覽器中使用 JavaScript 進行的。這會導致初始頁面加載時間變慢,但頁面轉換速度更快,因為只需要重新渲染必要的組件。

如何在我的客戶端渲染的 React 應用程序中發出服務器端請求?

要在客戶端渲染的 React 應用程序中發出服務器端請求,您可以使用 fetch API 或 axios 等庫。您可以在 componentDidMount 生命週期方法中或在使用函數組件時在 useEffect 掛鉤內發出請求。然後可以將響應設置為狀態並在您的組件中使用。

為什麼我的全局變量在 React 中執行兩次?

這可能是由於 React 批處理狀態更新的方式造成的。如果您在 React 組件內更新全局變量,則由於 setState 的異步性質,它可能會更新兩次。為避免這種情況,您可以使用 setState 的函數形式,這可以確保狀態更新基於先前狀態,而不是當前狀態。

如何在服務端渲染的 React 中使用異步 API?

要在服務端渲染的 React 中使用異步 API,您可以在服務器端代碼中使用 async/await 語法。這允許您在渲染頁面之前等待 API 響應。您可以使用 axios 等庫來發出 API 請求。

服務端渲染在 React 中有哪些好處?

服務端渲染在 React 中有很多好處。它提高了初始頁面加載時間,這可以帶來更好的用戶體驗。它還提高了 SEO,因為搜索引擎爬蟲可以更容易地索引服務端渲染的內容。此外,它允許更一致的初始狀態,因為相同的代碼在服務器和客戶端上運行。

如何在使用服務端渲染的 React 中的異步 API 時處理錯誤?

您可以使用 try/catch 塊在異步函數中處理錯誤。這允許您捕獲在發出 API 請求時發生的任何錯誤並適當地處理它們,例如通過渲染錯誤消息。

我可以在服務端渲染的 React 中使用鉤子嗎?

是的,您可以在服務端渲染的 React 中使用鉤子。但是,請記住,鉤子只能在函數組件中使用,而不能在類組件中使用。此外,某些鉤子(例如 useEffect)不會在服務器上運行,因此您需要確保您的代碼可以處理這種情況。

如何提高服務端渲染的 React 應用程序的性能?

提高服務端渲染的 React 應用程序性能的方法有很多。您可以使用代碼分割,只為每個頁面加載必要的代碼。您還可以使用緩存來避免重新渲染未更改的頁面。此外,優化服務器端代碼可以幫助提高性能。

如何測試我的服務端渲染的 React 應用程序?

您可以使用 Jest 和 React Testing Library 等測試庫來測試您的服務端渲染的 React 應用程序。這些庫允許您隔離測試組件並確保它們正確渲染。

我可以將服務端渲染與 Next.js 一起使用嗎?

是的,Next.js 是一個用於 React 的框架,它開箱即用地支持服務端渲染。它提供了一個簡單的服務端渲染 API,還支持靜態站點生成和客戶端渲染。

以上是處理服務器渲染的反應中的異步API的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1317
25
PHP教程
1268
29
C# 教程
1244
24
JavaScript的演變:當前的趨勢和未來前景 JavaScript的演變:當前的趨勢和未來前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

JavaScript引擎:比較實施 JavaScript引擎:比較實施 Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript:探索網絡語言的多功能性 JavaScript:探索網絡語言的多功能性 Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

如何使用Next.js(前端集成)構建多租戶SaaS應用程序 如何使用Next.js(前端集成)構建多租戶SaaS應用程序 Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

使用Next.js(後端集成)構建多租戶SaaS應用程序 使用Next.js(後端集成)構建多租戶SaaS應用程序 Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

從C/C到JavaScript:所有工作方式 從C/C到JavaScript:所有工作方式 Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

See all articles