一、描述

一般很少去使用 <script> 去加载 react,都是 ES6 的方式使用 npm 包加载,如果是 script 的方式, ReactDOM 是作为顶级 API 存在的,如果是 ES6 的组件式开发,如果 import ReactDOM from 'react-dom',如果是 ES5 的开发方式,可以使用 var ReactDOM = require('react-dom') '

react-dom 包提供了一系列用于 DOM 操作的方法,可以在应用程序的顶层使用,或者作为 React 模型或组件之外的操作 DOM 的一种 escape hatch 方式,一般大多数组件是不需要使用此模块的。

主要的 API如下:

  • render()
  • hydrate()
  • unmountComponentAtNode()
  • findDOMNode()
  • createPortal()

浏览器支持方面,React 支持所有流行的浏览器,包括 IE9 及其更高版本,尽管旧版浏览器(IE9/IE10)会需要一些 polyfill

二、render()

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

render 方法主要将 React element 渲染到提供的容器中的DOM 中,并且返回对组件的引用,如果是无状态组件,会返回 NULL

如果 React 之前已经渲染到容器中了,则会执行更新,并仅在必要的时候改变DOM以反映最新的 React 元素。

如果提供了可选回调方法,则将在呈现或更新组件后执行该回调。

如果你使用 create-react-app,则在 index.js 中,已经使用过了 ReactDOM.render 方法。

ReactDOM.render(<App3 />, document.getElementById('root'));

需要注意的是,ReactDOM.render 控制传入的容器节点的内容,第一次调用时,内部的任何现有DOM元素都会被替换,后来的调用使用 React 的 DOM diffing 算法进行有效的更新。

ReactDom.render 不修改容器节点(仅修改容器的子节点),可以将组件插入现有 DOM 节点,而不覆盖现有的子节点。

ReactDOM.render 当前返回对根 React Component 实例的引用,但是使用此返回值是一种遗留情况,应该避免去使用,因为在某些情况下,React 在未来的版本可能会使用异步渲染组件,因此返回值可能是失效的。如果需要对根 React Component 实例的引用,则首选解决方案是将会回调引用附加到根元素上面,具体的可以看看 callback ref 的文档:https://reactjs.org/docs/refs-and-the-dom.html#callback-refs

不应当使用 ReactDOM.render() 去 hydrate 一个服务端渲染的容器,这种方式在 React 17 中将被废弃,需要使用下面的 hydrate() API 替代。

三、hydrate()

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

hydrate 和 render 方法相同,但是用于 hydrate 由 ReactDomServer 渲染的容器内容。React 会尝试将事件侦听器附加到现有标记。

React 期望在服务器和客户端之前见渲染的内容是相同的,因此 hydrate 可以 patch 文本内容的差异,但是应将不匹配视为错误并且应当去 fix。在开发模式中,React 警告 hydrate 过程中的不匹配,如果不匹配,则无法保证 patch 属性差异。这对于性能原因很重要,因为在多数应用程序中,不匹配情况很少,因此验证所有标记将非常的昂贵。

如果服务器和客户端之间的单个元素的属性或者文本内容不可避免地不同(比如 timestamp),则可以通过向元素添加 suppressHydrationWarning = {true} 来静默警告。他只能在一个级别层次起作用,并且是作为 escape hatch 使用的,不应该去过渡使用这种方式,除非是文本内容,否则 React 仍然不会去尝试做 patch,所以可能会保持不一致,知道将来的某个时间点进行更新。

如果一定要在服务器和客户端渲染不同的内容,则可以执行两次渲染。在客户端上渲染不同内容的组件读取如同 this.state.isClient 这样的状态变量,你可以在 componentDidMount 中将其设置为 true。这样初始渲染过程将呈现于服务器相同的内容,避免不匹配的情况,但在 hydrate 之后或立即同步发生其他的一些过程。需要注意的是,这种方式会让组件变慢,因为必须渲染两次,需要谨慎使用。

需要格外注意在网络环境差的情况下的用户体验, JavaScript 代码的加载时间可能比初始化 HTML 渲染要晚的多,因此如果在仅客户端传递传递中呈现不同的内容,则转换可能是不稳定的。但是,如果执行的好的话,在服务器上呈现应用程序的 shell 是有益的,并且仅在客户端上显示一些额外的小部件。要了解如何在不解决标记不匹配问题的情况下执行此操作,可以看上面那段文字的说明。

四、unmountComponentAtNode()

ReactDOM.unmountComponentAtNode(container)

这个方法会从 DOM 中删除已经安装的 React 组件并且清理其事件处理程序和状态,如果容器中没有安装任何组件,则调用此函数不执行任何操作。如果卸载了组件,则返回 true,如果没有要卸载的组件则返回 false

五、findDOMNode()

findDOMNode 是用来访问底层DOM节点的转义填充,在大多数情况下,不鼓励使用这种方式,因为会打破组件的抽象,在严格模式中,这个 API 已经被废弃。

ReactDOM.findDOMNode(component)

如果此组件已经挂载到 DOM 中,则返回相应的本机浏览器的 DOM 元素,这个方法对于从 DOM 中取值或者 attrs 非常有用,例如表单字段值和执行 DOM 测量操作,在大多数情况下,你可以将应用附加到 DOM 节点上,然后可以完全避免使用 findDOMNode。(也就是使用 ref={el = > this.el = el;}

当组件渲染成 false 或者 null 的时候,findDOMNode 会返回 null,当组件呈现给字符串时,findDOMNode 返回包含该值的文本 DOM 节点。从 React 16 开始,组件可以返回具有多个子节点的片段,在这种情况下,findDOMNode 将返回与第一个非空子节点对应 DOM 节点。

findDOMNode 仅适用于已安装的组件(也就是挂载到了 DOM 中组件)如果您尝试在未安装组件上调用此方法(如在尚未 create 的组件上调用 render 中的 findDOMNode 将引发异常)

findDOMNode 是不能够用于功能组件的。

六、createProtal()

ReactDOM.createPortal(child, container)

创建 protal,Portal 提供了一种将子项成纤维存在于 DOM 组件层次接口之外的 DOM 节点的方法, 这个我之前已经写过相关的文章了,可以看:http://www.ptbird.cn/react-portal-createPortal.html