验证码: 看不清楚,换一张 查询 注册会员,免验证
  • {{ basic.site_slogan }}
  • 打开微信扫一扫,
    您还可以在这里找到我们哟

    关注我们

React SSR架构Streaming Render与Selective Hydration源码分析

阅读:976 来源:乙速云 作者:代码code

React SSR架构Streaming Render与Selective Hydration源码分析

Streaming Render

以下 Demo 使用的 react 及 react-dom 版本为 16.14.0

假设我们有如下应用:

import React from 'react'
const Item = () => {
  const start = Date.now()
  // 人为增加该组件的渲染时间
  while (Date.now() - start < 2) {}
  return 
  • a
  • } const App = () => {   return (     
            List:       {[...new Array(3700)].map((_, i) => {         return        })}     
      ) } export default App

    我们使用 renderToString 来进行服务端渲染:

    app.get('/string', async (req, res) => {
      const markup = renderToString()
      res.end(`
        
          
            
              Demo
              
            
            
              ${markup}
                        `) })

    通过浏览器访问,可以看到需要等待比较长的时间页面才显示出所有内容

    我们换成 renderToNodeStream 再试一试:

    app.get('/node_stream', (req, res, next) => {
      res.write(`
          
            
              Demo
              
            
            
              `)
      const stream = renderToNodeStream()
      stream.pipe(res, {end: false})
      stream.on('end', () => {
        res.write(`
                       `)     res.end()   }) })

    可以看到浏览器中先显示了一部分内容,然后才显示所有内容

    这样,当用户访问一个大型的 React 页面时,可以让其尽早地看到一部分内容,从而提供一个比较好的用户体验。

    其原理主要是利用了 http 的 Transfer-Encoding: chunked  响应头。

    那么,React 每次返回多少内容呢?通过断点调试可以知道,这个值是 16 KB。但是实际返回的长度可能会略大于 16 KB,因为 React 总是会完整的返回标签,比如 ,而不是拆成两部分 li>

    React SSR架构Streaming Render与Selective Hydration源码分析

    为了能够让开发更好的控制流式渲染每次返回的内容,我们可以结合 Suspense,但是由于 16 版本不支持 SSR 使用 Suspense,所以接下来我们换成 v18.2.0 继续我们的实验。

    假设我们有如下应用,期待的效果是用户先看到 List:Loading,4 秒后显示 List:a

    import React, {Suspense} from 'react'
    const Item = ({index}) => {
      return 'a'
    }
    const Comp = React.lazy(
      () =>
        new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve({default: Item})
          }, 4000)
        })
    )
    const App = () => {
      return (
        
            List:                            
      ) } export default App

    我们先用 renderToNodeStream 试试,可以看到,页面并没有像我们所期待的那样,而是一开始显示空白,4 秒后才显示 List:a,这实际上已经失去 Streaming Render 的功能了,所以这个函数在 React 18 中被标记为了 deprecated,使用 renderToPipeableStream 来替代。

    我们换成 renderToPipealbeStream 就可以看到我们期望的效果了

    其原理也很简单,第一次返回的内容为:

    第二次返回的内容为:

    a
    

    其中