最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React?SSR架構(gòu)Stream?Rendering與Suspense?for?Data?Fetching

 更新時(shí)間:2023年03月27日 14:02:16   作者:Aaaaaaaaaaayou  
這篇文章主要為大家介紹了React?SSR架構(gòu)Stream?Rendering與Suspense?for?Data?Fetching解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

之前寫(xiě)過(guò)一篇文章介紹了 React 18 SSR 新架構(gòu),今天繼續(xù)基于此架構(gòu)實(shí)戰(zhàn)一下。假設(shè)我們的業(yè)務(wù)背景如下:

我們的頁(yè)面分為兩大塊,上面部分是個(gè)人介紹,依賴(lài)接口 /api/profile (耗時(shí)約 3s),下面部分是文章列表,依賴(lài)接口 /api/list(耗時(shí)約 6s)。其中文章列表的業(yè)務(wù)邏輯非常重,代碼體積很大,依賴(lài)的接口比較慢。

我們先來(lái)看下傳統(tǒng)的 SSR (服務(wù)端獲取到所有接口數(shù)據(jù)后調(diào)用 renderToString 渲染出內(nèi)容返回給前端,同時(shí)在頁(yè)面中插入全局的 INITIAL_STATE 供客戶(hù)端注水)和基于 Stream Rendering & Suspense for Data Fetching (以下簡(jiǎn)稱(chēng) Stream SSR)兩者的效果對(duì)比:

傳統(tǒng) SSRStream SSr

首先,我們來(lái)對(duì)比下從用戶(hù)發(fā)起請(qǐng)求到用戶(hù)看到內(nèi)容這個(gè)階段。傳統(tǒng) SSR 用戶(hù)看到的都是一個(gè)空白頁(yè)面,一直要等到最耗時(shí)的 /api/list 接口返回用戶(hù)才能看到內(nèi)容。而 Stream SSR 有以下幾點(diǎn)的提升:

  • 在頁(yè)面內(nèi)容返回前有 loading 的提示
  • Profile 的內(nèi)容先處理完,先返回,沒(méi)有被 List 阻塞

同樣的,注水過(guò)程也是如此。傳統(tǒng) SSR 需要等到 JS 加載完后,統(tǒng)一對(duì)整個(gè)應(yīng)用進(jìn)行注水。而 Stream SSR 則先完成了 Profile 的注水。

那么,要怎么實(shí)現(xiàn)這樣的效果呢?接下來(lái)讓我們 step by step?;蛘咧苯涌?a rel="external nofollow" target="_blank">代碼。

React SSR Stream Rendering & Suspense for Data Fetching 實(shí)踐

Stream Rendering

首先,為了實(shí)現(xiàn) Stream Rendering,我們需要使用 renderToPipeableStream,假設(shè)我們有如下 HTML 模板:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>SSR + MicroFrontend</title>
  </head>
  <body>
    <div id="app1"><!-- app1 --></div>
    <script crossorigin src="http://localhost:8080/dist/client.js"></script>
  </body>
</html>

則我們可以按如下方式進(jìn)行返回:

app.get('/', async (req, res) => {
  const [heal, tail] = html.split('<!-- app1 -->')
  const stream = new Writable({
    write(chunk, _encoding, cb) {
      res.write(chunk, cb)
    },
    final() {
      res.end(tail)
    },
  })
  const {pipe} = renderToPipeableStream(<App />, {
    onShellReady() {
      res.statusCode = 200
      res.write(head)
      pipe(stream)
    },
  })
})

看著有點(diǎn)奇怪是吧,這是因?yàn)?renderToPipeableStream 的返回不再是 Node.js 中的 ReadableStream 對(duì)象,無(wú)法監(jiān)聽(tīng) end 事件。所以這里通過(guò)一個(gè)中間的 Writable 對(duì)象來(lái)轉(zhuǎn)接數(shù)據(jù),并監(jiān)測(cè)渲染流的結(jié)束。

Stream Rendering 的部分搞定了,接下來(lái)我們看看 Data Fetching 部分。

Suspense for Data Fetching

這篇文章曾經(jīng)提到過(guò)結(jié)合 Suspense 做 Data Fetching,但是之前是自己實(shí)現(xiàn)的一個(gè)簡(jiǎn)單的請(qǐng)求工具,為了更貼近實(shí)際,這次使用 react-query。則組件中可以按照如下方式來(lái)請(qǐng)求數(shù)據(jù):

async function getList() {
  const rsp = await fetch('http://localhost:9000/api/list')
  const data = await rsp.json()
  return data
}
const List = () => {
  const query = useQuery(['list'], getList)
  return (
    <ul>
      {query.data.map((item) => (
        <li key={item.name}>{item.name}</li>
      ))}
    </ul>
  )
}

在使用該組件的時(shí)候,可以用 Suspense 包裹起來(lái),以便于數(shù)據(jù)返回前用戶(hù)可以看到一個(gè) loading 的效果:

const App = () => {
  return (
    <div>
      <Suspense fallback={<p>Loading List...</p>}>
        <List />
      </Suspense>
      ...
    </div>
  )
}

同時(shí)為了減少入口文件的體積,我們通過(guò)異步的方式來(lái)引入 List 這個(gè)比較大的組件:

const List = React.lazy(() => import('./List'))

類(lèi)似的,Profile 組件也可以按照同樣的方式來(lái)處理。

這樣,Stream Rendering & Suspense for Data Fetching 基本上算是實(shí)現(xiàn)了。不過(guò)現(xiàn)在還有個(gè)問(wèn)題,對(duì)于每個(gè)組件,我們會(huì)分別在服務(wù)端和客戶(hù)端都請(qǐng)求一次接口。正確的做法應(yīng)該是只在服務(wù)端請(qǐng)求一次,然后服務(wù)端返回 HTML 的時(shí)候把接口數(shù)據(jù)也一并帶上,作為 CSR 的初始數(shù)據(jù)。

React Query 官網(wǎng)中有介紹 SSR 相關(guān)的內(nèi)容,但是跟傳統(tǒng)的 SSR 沒(méi)什么區(qū)別,也是要等到數(shù)據(jù)都獲取完后,才開(kāi)始渲染:

 function handleRequest (req, res) {
   const queryClient = new QueryClient()
   await queryClient.prefetchQuery('key', fn)
   const dehydratedState = dehydrate(queryClient) // 得到一個(gè)接口請(qǐng)求的全局狀態(tài)
   const html = ReactDOM.renderToString(
     <QueryClientProvider client={queryClient}>
       <Hydrate state={dehydratedState}>
         <App />
       </Hydrate>
     </QueryClientProvider>
   )
   res.send(`
     <html>
       <body>
         <div id="root">${html}</div>
         <script>
           window.__REACT_QUERY_STATE__ = ${JSON.stringify(dehydratedState)};
         </script>
       </body>
     </html>
   `)
   queryClient.clear()
 }

這樣的做法有幾個(gè)缺點(diǎn):

  • 整個(gè)應(yīng)用的渲染都被阻塞了,原本可以更早返回的 Profile 也被推遲了
  • 必須要知道當(dāng)前頁(yè)面渲染所需要調(diào)用的所有接口,當(dāng)頁(yè)面很復(fù)雜且由多人維護(hù)時(shí)這個(gè)代碼就很不好維護(hù)了

下面我們來(lái)解決這些問(wèn)題,最終的方案我稱(chēng)之為“全局狀態(tài)動(dòng)態(tài)更新”方案。

全局狀態(tài)動(dòng)態(tài)更新

從上面的代碼可以知道,通過(guò) dehydrate(queryClient) 可以得到一個(gè)全局的對(duì)象用來(lái)描述當(dāng)前請(qǐng)求得到的數(shù)據(jù),那我們是不是可以在組件里面每次有數(shù)據(jù)獲取到時(shí)就來(lái)更新一下這個(gè)對(duì)象呢?就像這樣:

const query = useQuery(['data'], getList)
const ee = useContext(EventEmitterContext)
if (ee && query.data) {
  ee.emit('updateState')
}

然后我們?cè)谔幚碚?qǐng)求的回調(diào)函數(shù)中監(jiān)聽(tīng)這個(gè)事件,更新全局狀態(tài):

  const templateDOM = new JSDOM(`
<!DOCTYPE html>
<html lang="en">
  <head>
  ...
  </head>
  <body>
    <div id="app1"><!-- app1 --></div>
    <script id="reactQueryState">window.__REACT_QUERY_STATE__ = ${JSON.stringify(
      dehydratedState
    )};</script>
    ...
  </body>
</html>
`)
...
ee.on('updateState', () => {
  const dehydratedState = dehydrate(queryClient)
  templateDoc.querySelector(
    '#reactQueryState'
  ).innerHTML = `window.__REACT_QUERY_STATE__ = ${JSON.stringify(
    dehydratedState
  )};`
})

這樣我們就做到了仍然流式的返回內(nèi)容給用戶(hù),并在這個(gè)過(guò)程中不停的更新全局?jǐn)?shù)據(jù),最后返回給客戶(hù)端,而且也不需要了解這個(gè)頁(yè)面渲染所需要的所有接口,即保證了用戶(hù)體驗(yàn),又沒(méi)有丟失代碼的可維護(hù)性。

React 團(tuán)隊(duì)一直在提高用戶(hù)體驗(yàn)、代碼可維護(hù)性和性能這些方面進(jìn)行不停的探索,感覺(jué)不久的將來(lái)前端的開(kāi)發(fā)方式又要被他們改變了,看來(lái)前端還沒(méi)有死,還能繼續(xù)折騰。

以上就是React SSR架構(gòu)Stream Rendering與Suspense for Data Fetching的詳細(xì)內(nèi)容,更多關(guān)于React SSR架構(gòu)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 關(guān)于antd tree和父子組件之間的傳值問(wèn)題(react 總結(jié))

    關(guān)于antd tree和父子組件之間的傳值問(wèn)題(react 總結(jié))

    這篇文章主要介紹了關(guān)于antd tree 和父子組件之間的傳值問(wèn)題,是小編給大家總結(jié)的一些react知識(shí)點(diǎn),本文通過(guò)一個(gè)項(xiàng)目需求實(shí)例代碼詳解給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-06-06
  • react實(shí)現(xiàn)拖拽模態(tài)框

    react實(shí)現(xiàn)拖拽模態(tài)框

    這篇文章主要為大家詳細(xì)介紹了react實(shí)現(xiàn)拖拽模態(tài)框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 在?React?項(xiàng)目中全量使用?Hooks的方法

    在?React?項(xiàng)目中全量使用?Hooks的方法

    這篇文章主要介紹了在?React?項(xiàng)目中全量使用?Hooks,使用 Hooks 能為開(kāi)發(fā)提升不少效率,但并不代表就要拋棄 Class Component,依舊還有很多場(chǎng)景我們還得用到它,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • react使用websocket實(shí)時(shí)通信方式

    react使用websocket實(shí)時(shí)通信方式

    這篇文章主要介紹了react使用websocket實(shí)時(shí)通信方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 基于React.js實(shí)現(xiàn)簡(jiǎn)單的文字跑馬燈效果

    基于React.js實(shí)現(xiàn)簡(jiǎn)單的文字跑馬燈效果

    剛好手上有一個(gè)要實(shí)現(xiàn)文字跑馬燈的react項(xiàng)目,然后ant-design上面沒(méi)有這個(gè)組件,于是只能自己手?jǐn)]一個(gè),文中的實(shí)現(xiàn)方法講解詳細(xì),希望對(duì)大家有所幫助
    2023-01-01
  • create-react-app全家桶router?mobx全局安裝配置

    create-react-app全家桶router?mobx全局安裝配置

    這篇文章主要為大家介紹了create-react-app全家桶router?mobx全局安裝配置,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 解決React報(bào)錯(cuò)`value` prop on `input` should not be null

    解決React報(bào)錯(cuò)`value` prop on `input` should&

    這篇文章主要為大家介紹了React報(bào)錯(cuò)`value` prop on `input` should not be null解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • React 性能優(yōu)化之非必要的渲染問(wèn)題解決

    React 性能優(yōu)化之非必要的渲染問(wèn)題解決

    本文主要介紹了React 性能優(yōu)化之非必要的渲染問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 淺談React 屬性和狀態(tài)的一些總結(jié)

    淺談React 屬性和狀態(tài)的一些總結(jié)

    下面小編就為大家?guī)?lái)一篇淺談React 屬性和狀態(tài)的一些總結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-11-11
  • React Native項(xiàng)目框架搭建的一些心得體會(huì)

    React Native項(xiàng)目框架搭建的一些心得體會(huì)

    React Native使你能夠在Javascript和React的基礎(chǔ)上獲得完全一致的開(kāi)發(fā)體驗(yàn),構(gòu)建世界一流的原生APP。接下來(lái)通過(guò)本文給大家分享React Native項(xiàng)目框架搭建的一些心得體會(huì),感興趣的朋友跟隨小編一起看看吧
    2021-05-05

最新評(píng)論

襄垣县| 娄烦县| 双牌县| 宁晋县| 万山特区| 宁乡县| 霍城县| 黑河市| 临湘市| 贵阳市| 乌鲁木齐市| 潍坊市| 博野县| 建水县| 白河县| 会泽县| 景泰县| 东阿县| 中江县| 建德市| 贵德县| 贺州市| 太和县| 黔东| 郧西县| 曲沃县| 盐源县| 大余县| 阿尔山市| 峨眉山市| 固安县| 谷城县| 称多县| 通许县| 静乐县| 铜陵市| 丁青县| 屯门区| 乐安县| 淅川县| 旬阳县|