要點總結
- 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') )
問題:
- 這是一個尋找根元素的 DOM 渲染。這在我的服務器上不存在,因此我們必須將其分開。
- 我們無法訪問主根元素之外的任何內容。我們無法設置 Facebook 標籤、標題、描述、各種 SEO 標籤,並且我們無法控制元素外部的其餘 DOM,尤其是頭部。
- 我們提供了一些狀態,但服務器和客戶端具有不同的狀態。我們需要考慮如何處理該狀態(在本例中為 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中文網其他相關文章!

JavaScript字符串替換方法詳解及常見問題解答 本文將探討兩種在JavaScript中替換字符串字符的方法:在JavaScript代碼內部替換和在網頁HTML內部替換。 在JavaScript代碼內部替換字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 該方法僅替換第一個匹配項。要替換所有匹配項,需使用正則表達式並添加全局標誌g: str = str.replace(/fi

本教程向您展示瞭如何將自定義的Google搜索API集成到您的博客或網站中,提供了比標準WordPress主題搜索功能更精緻的搜索體驗。 令人驚訝的是簡單!您將能夠將搜索限制為Y

本文系列在2017年中期進行了最新信息和新示例。 在此JSON示例中,我們將研究如何使用JSON格式將簡單值存儲在文件中。 使用鍵值對符號,我們可以存儲任何類型的

因此,在這裡,您準備好了解所有稱為Ajax的東西。但是,到底是什麼? AJAX一詞是指用於創建動態,交互式Web內容的一系列寬鬆的技術。 Ajax一詞,最初由Jesse J創造

增強您的代碼演示文稿:10個語法熒光筆針對開發人員在您的網站或博客上共享代碼段的開發人員是開發人員的常見實踐。 選擇合適的語法熒光筆可以顯著提高可讀性和視覺吸引力。 t

利用輕鬆的網頁佈局:8 ESTISSEL插件jQuery大大簡化了網頁佈局。 本文重點介紹了簡化該過程的八個功能強大的JQuery插件,對於手動網站創建特別有用

本文介紹了關於JavaScript和JQuery模型視圖控制器(MVC)框架的10多個教程的精選選擇,非常適合在新的一年中提高您的網絡開發技能。 這些教程涵蓋了來自Foundatio的一系列主題

核心要點 JavaScript 中的 this 通常指代“擁有”該方法的對象,但具體取決於函數的調用方式。 沒有當前對象時,this 指代全局對象。在 Web 瀏覽器中,它由 window 表示。 調用函數時,this 保持全局對象;但調用對象構造函數或其任何方法時,this 指代對象的實例。 可以使用 call()、apply() 和 bind() 等方法更改 this 的上下文。這些方法使用給定的 this 值和參數調用函數。 JavaScript 是一門優秀的編程語言。幾年前,這句話可


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

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

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

記事本++7.3.1
好用且免費的程式碼編輯器

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。