乐趣区

关于ssr:精通-React-SSR-之-API-篇

写在后面

React 提供的 SSR API 分为两局部,一部分面向服务端(react-dom/server),另一部分仍在客户端执行(react-dom

<img src=”http://cdn.ayqy.net/data/home/qxu1001840309/htdocs/cms/wordpress/wp-content/uploads/2020/11/1_VG33xLBOqcpfctgiyh0jtA.png” alt=”react ssr” width=”625″ height=”446″ class=”size-large wp-image-2317″ />

一.ReactDOMServer

ReactDOMServer相干 API 可能 在服务端将 React 组件渲染成动态的(HTML)标签

The ReactDOMServer object enables you to render components to static markup.

把组件树渲染成对应 HTML 标签的工作在浏览器环境也能实现,因而,面向服务端的 React DOM API 也分为两类:

  • 能跨 Node.js、浏览器环境运行的 String API:renderToString()、renderToStaticMarkup()
  • 只能在 Node.js 环境运行的 Stream API:renderToNodeStream()、renderToStaticNodeStream()

renderToString

ReactDOMServer.renderToString(element)

最根底的 SSR API,输出 React 组件(精确来说是ReactElement),输入 HTML 字符串。之后由客户端 hydrate API 对服务端返回的视图构造附加上交互行为,实现页面渲染:

If you call ReactDOM.hydrate() on a node that already has this server-rendered markup, React will preserve it and only attach event handlers.

renderToStaticMarkup

ReactDOMServer.renderToStaticMarkup(element)

renderToString 相似,区别在于 API 设计上,renderToStaticMarkup只用于纯展现(没有事件交互,不须要 hydrate)的场景

This is useful if you want to use React as a simple static page generator, as stripping away the extra attributes can save some bytes. If you plan to use React on the client to make the markup interactive, do not use this method. Instead, use renderToString on the server and ReactDOM.hydrate() on the client.

因而 renderToStaticMarkup 只生成洁净的 HTML,不带额定的 DOM 属性(如 data-reactroot),响应体积上有 些微 的劣势

之所以说体积劣势些微,是因为在 React 16 之前,SSR 采纳的是基于字符串校验和(string checksum)的 HTML 节点复用形式,字对字地严格校验一致性,一旦发现不匹配就齐全抛弃服务端渲染后果,在客户端从新渲染:

If for any reason there’s a mismatch, React raises a warning in development mode and replaces the entire tree of server-generated markup with HTML that has been generated on the client.

生成了大量的额定属性:

// renderToString
<div data-reactroot=""data-reactid="1"data-react-checksum="122239856">
  <!-- react-text: 2 -->This is some <!-- /react-text -->
  <span data-reactid="3">server-generated</span>
  <!-- react-text: 4--> <!-- /react-text -->
  <span data-reactid="5">HTML.</span>
</div>

这时候 renderToStaticMarkup 生成洁净清新的 HTML 还有着不小的体积劣势:

// renderToStaticMarkup
<div data-reactroot="">
  This is some <span>server-generated</span> <span>HTML.</span>
</div>

而 React 16 改用单节点校验来复用(服务端返回的)HTML 节点,不再生成 data-reactiddata-react-checksum 等体积占用小户,两个 API 渲染后果的体积差别变得微不足道。例如,对于 React 组件:

class MyComponent extends React.Component {
  state = {title: 'Welcome to React SSR!',};
  }

  render() {
    return (
      <div>
        <h1 className="here">
          {this.state.title} Hello There!
        </h1>
      </div>
    );
  }
}

二者的渲染后果别离为:

// renderToString
<div data-reactroot=""><h1 class="here">Welcome to React SSR!<!-- --> Hello There!</h1></div>

// renderToStaticMarkup
<div><h1 class="here">Welcome to React SSR! Hello There!</h1></div>

也就是说,目前(2020/11/8,React 17.0.1)renderToStaticMarkuprenderToString 的理论差别次要在于:

  • renderToStaticMarkup不生成data-reactroot
  • renderToStaticMarkup不在相邻文本节点之间生成<!-- -->(相当于合并了文本节点,不思考节点复用,算是针对动态渲染的额定优化措施)

renderToNodeStream

ReactDOMServer.renderToNodeStream(element)

对应于 renderToString 的 Stream API,将 renderToString 生成的 HTML 字符串以 Node.js Readable stream 模式返回

P.S. 默认返回utf-8 编码的字节流,其它编码格局需自行转换

P.S. 该 API 的实现依赖 Node.js 的 Stream 个性,所以不能在浏览器环境应用

renderToStaticNodeStream

ReactDOMServer.renderToStaticNodeStream(element)

对应于 renderToStaticMarkup 的 Stream API,将 renderToStaticMarkup 生成的洁净 HTML 字符串以 Node.js Readable stream 模式返回

P.S. 同样按 utf-8 编码,并且不能在浏览器环境应用

二.ReactDOM

hydrate()

ReactDOM.hydrate(element, container[, callback])

与罕用的 render() 函数签名完全一致:

ReactDOM.render(element, container[, callback])

hydrate()配合 SSR 应用,与 render() 的区别在于 渲染过程中可能复用服务端返回的现有 HTML 节点,只为其附加交互行为(事件监听等),并不从新创立 DOM 节点:

React will attempt to attach event listeners to the existing markup.

须要留神的是,服务端返回的 HTML 与客户端渲染后果不统一时,出于性能思考,hydrate()并不纠正除文本节点外的 SSR 渲染后果,而是一误再误

There are no guarantees that attribute differences will be patched up in case of mismatches. This is important for performance reasons because in most apps, mismatches are rare, and so validating all markup would be prohibitively expensive.

只在 development 模式下对这些不统一的问题报 Warning,因而必须器重 SSR HydrationWarning,要当 Error 一一解决:

This performance optimization means that you will need to make extra sure that you fix any markup mismatch warnings you see in your app in development mode.

非凡的,对于意料之中的不统一问题,例如工夫戳,可通过 suppressHydrationWarning={true} 属性显式疏忽该元素的 HydrationWarning(只是疏忽正告,并不纠错,所以仍保留服务端渲染后果)。如果非要在服务端和客户端别离渲染不同的内容,倡议先保障首次渲染内容统一,再通过更新来实现(当然,性能会稍差一点),例如:

class MyComponent extends React.Component {
  state = {isClient: false}

  render() {return this.state.isClient ? '渲染... 客户端内容' : '渲染... 服务端内容';}

  componentDidMount() {
    this.setState({isClient: true});
  }
}

三.SSR 相干的 API 限度

大部分生命周期函数在服务端都不执行

SSR 模式下,服务端只执行 3 个生命周期函数

  • constructor
  • getDerivedStateFromProps
  • render

其余任何生命周期在服务端都不执行,包含 getDerivedStateFromErrorcomponentDidCatch 等错误处理 API

<img src=”http://cdn.ayqy.net/data/home/qxu1001840309/htdocs/cms/wordpress/wp-content/uploads/2020/11/96C4B5DD-20B3-43F9-A8C6-9B220EB2C0A5.png” alt=”react ssr lifecycle” width=”625″ height=”355″ class=”size-large wp-image-2319″ />

P.S. 曾经废除的 componentWillMountUNSAFE_componentWillMountgetDerivedStateFromPropsgetSnapshotBeforeUpdate互斥,若存在后一组新 API 中的任意一个,就不会调用前两个旧 API

不反对 Error Boundary 和 Portal

With streaming rendering it’s impossible to “call back” markup that has already been sent, and we opted to keep renderToString and renderToNodeStream’s output identical.

为了反对流式渲染,同时放弃 String API 与 Stream API 输入内容的一致性,就义了会引发渲染回溯的两大个性

  • Error Boundary:可能捕捉子孙组件的运行时谬误,并渲染一个降级 UI
  • Portal:可能将组件渲染到指定的任意 DOM 节点上,同时保留事件按组件层级冒泡

很容易了解,流式边渲染边响应,无奈(回溯回去)批改曾经收回去的内容 ,所以其它相似的场景也不反对,比方渲染过程中动静往head 里插个 stylescript标签

P.S. 对于 SSR Error Boundary 的更多探讨,见 componentDidCatch doesn’t work in React 16’s renderToString

参考资料

  • ReactDOMServer
  • ReactDOM
  • The Component Lifecycle
  • What’s New With Server-Side Rendering in React 16
  • react lifecycle methods diagram

有所得、有所惑,真好

关注「前端向后」微信公众号,你将播种一系列「用 原创」的高质量技术文章,主题包含但不限于前端、Node.js 以及服务端技术

本文首发于 ayqy.net,原文链接:http://www.ayqy.net/blog/reac…

退出移动版