React 的虚拟 DOM 是其可被用于服务端渲染的关键。首先,每个 React 组件在虚拟 DOM 中完成渲染,然后 React 通过虚拟 DOM 来更新浏览器 DOM 中产生变化的那一部分。虚拟 DOM 作为内存中的 DOM 表现,为 React 在 Node.js 这类非浏览器环境下的运行提供了可能。React 可以从虚拟 DOM 中生成一个字符串,而不是更新真正的 DOM。这使得我们可以在客户端和服务端使用同一个 React 组件
React 提供了两个可用于服务端渲染组件的函数: React.renderToString()
和 React.renderToStaticMarkup()
。
在设计用干服务端渲染的 React 组件时需要有预见性,需要考虑以下方面。
服务端渲染对于搜索引擎优化来说至关重要。因为服务端不存在 DOM,所以无法使用标准的 React.render()
方法来渲染 React 组件,因此 React 还提供了两个渲染函数,它们支持标准 React 组件生命周期方法的一个子集,因而能够实现服务端渲染。
React.renderToString()
是两个服务端渲染函数中的一个,也是开发中主要使用的一个函数。和 React.render()
不同,该函数去掉了用于表示渲染位置的参数。取而代之,该函数只返回一个字符串。这是一个快速的同步(阻塞式)函数,非常快。
var MyComponent = React.createClass({ render: function(){ return <div>Hello World!</div>; } }); var world = React.renderToString(<MyComponent/>); <div data-reactid=".fgvrzhg2yo" data-react-checksum="-1663559667"> Hello World! </div>
你会注意到,React 为这个 <div>
元素添加了两个 data
前缀的属性。
在浏览器环境下,React 使用 data-reactid
来区分 DOM 节点。这也是每当组件的 state
及 props
发生变化时,React 都可以精准地更新指定 DOM 节点的原因。
data-react-checksum
仅仅存在于服务端。顾名思义,它是已创建 DOM 的校验和。这准许 React 在客户端复用与服务端结构上相同的 DOM 结构。该属性只会添加到根元素上。
React.renderToStaticMarkup()
是第二个服务端渲染函数。
除了不会包含 React 的 data
属性外,它和 React.renderToString()
没有区别。
var MyComponent = React.createClass({ render: function(){ return <div>Hello World!</div> } }); var world = React.renderToStaticMarkup(<MyComponent/>); <div>Hello World!</div>
React.renderToString()
还是用 React.renderToStaticMarkup()
每个渲染函数都有自己的用途,所以你必须明确自己的需求,再去决定使用哪个渲染函数。当且仅当你不打算在客户端渲染这个 React 组件时,才应该选择使用 React.renderToStaticMarkup()
函数。
下面是一些示例:
大多数情况下,我们都会选择使用 React.renderToString()
。这将准许 React 使用 data-react-checksum
在客户端更迅速地初始化同一个 React 组件因为 React 可以重用服务端提供的 DOM,所以它可以跳过生成 DOM 节点以及把它们挂载到文档中这两个昂贵的进程。对于复杂些的站点,这样做会显著地减少加载时间,用户可以更快地与站点进行交互。
确保 React 组件能够在服务端和客户端准确地渲染出一致的结构是很重要的。如果 data-react-checksum
不匹配,React 会舍弃服务端提供的 DOM,然后生成新的 DOM 节点,并且将它们更新到文档中。此时,React 也不再拥有服务端渲染带来的各种性能上的优势。
一旦渲染为字符串,组件就会只调用位于 render()
之前的组件生命周期方法。需要指出, componentDidMount()
和 componentWillUnmount()
不会在服务端渲染过程中被调用,而 componentWillMount()
在两种渲染方式下均有效。
当新建一个组件时,你需要考虑到它可能既在服务端又在客户端进行渲染。这一点在创建事件监听器时尤为重要,因为并不存在一个生命周期方法会通知我们该 React 组件是否已经走完了整个生命周期。
在 componentWillMount()
内注册的所有事件监听器及定时器都可能潜在地导致服务端内存泄漏。
服务端渲染对于搜索引擎优化来说至关重要。而 React 支持在服务端和客户端浏览器中渲染相同的 React 组件而要有效地做到这一点,则需要保证整个应用都使用这一架构方式以支持服务端渲染。