React 相关方法(API)介绍-ReactDom、ReactDOMServer、子节点

本文介绍 React 中 ReactDOM 和 ReactDOMServer 类库的功能,包括组件的客户端和服务端渲染方法,以及如何通过 React.Children 处理组件子节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

React组件及子组件最终通过render方法渲染到DOM中,该方法由ReactDOM类库提供。页面的渲染可以在客户端或服务端完成,ReactDOMServer类库使你可以在服务端完成组件的渲染。通过this.props.children属性可以访问组件的子节点,而对子节点的处理的方法则由React.Children类提供。


  1. ReactDOM类库
  2. ReactDOMServer类库
  3. React.Children


1. ReactDOM类库

react-dom包提供了DOM操作的具体方法,如:组件渲染、节点找查等。你可以在程序的最顶层使用它,除了必要的DOM操作,大多数组件不需要使用这个模块。

1.1 渲染ReactElementReactDOM.render
ReactComponent render(
  ReactElement element,
  DOMElement container,
  [function callback]
)

渲染一个ReactElement到DOM中

  • element,要渲染的的ReactElement
  • container,渲染结果在DOM中插入的位置
  • callback,回调函数,可选。传入该值时,会在组件渲染完或更新完成后调用
  • 返回值:React组件

如,渲染一个组件到DOM中(render.html):

var App = React.createClass({
  render: function () {
    return (
	<div className="divider">
	  <h2>{this.props.children}</h2><hr/>
	</div>
  )}
});

ReactDOM.render(
  <App>itbilu.com</App>,
  document.getElementById('example'),
  function(){
	console.log('rendered done');  
  }
);
// rendered done


1.2 移除组件:ReactDOM.unmountComponentAtNode
boolean unmountComponentAtNode(DOMElement container)
  • container,要清除组件的container
  • 返回值:true或false

移除一个已经加载到DOM中的React组件,并清除对应的事件处理器和状态。如果container中没有组件,则不会做任何操作。

示例,从DOM中移除React组件(unmountComponentAtNode.html):

ReactDOM.render(
  <div className="divider">
	<h2>itbilu.com</h2><hr/>
  </div>,
  document.getElementById('example')
);
var result = ReactDOM.unmountComponentAtNode(document.getElementById('example'));
console.log(result);  // true


1.3 查找节点:findDOMNode()
DOMElement findDOMNode(ReactComponent component)
  • component,React组件或DOM元素
  • 返回值:DOM元素或null

当组件已经挂载到DOM中,会返回浏览器中的DOM元素。这个方法从DOM中读取值时很有用,如:读取DOM的尺寸等。大多数情况下,应该使用DOM的固定引用而非使用findDOMNode方法。当render返回null时findDOMNode也会返回null。

示例,使用findDOMNode方法查找渲染到DOM中的组件:

var App = React.createClass({
  render: function () {
    return (
	<div className="divider">
	  <h2>{this.props.children}</h2><hr/>
	</div>
  )}
});

var reactEle = ReactDOM.render(
  <App>itbilu.com</App>,
  document.getElementById('example')
);

var element = ReactDOM.findDOMNode(reactEle);


2. ReactDOMServer类库

react-dom/server包提供了组件的服端渲染功能。

2.1 渲染为HTML:renderToString()
string renderToString(ReactElement element)
  • element,React元素
  • 返回值:HTML

渲染ReactElement为原始HTML,这个方法只应该用在服务器端(虽然客户端也可以用)。你可以使用这个方法在服务端生成HTML字符串,并在页面请求时返回以使页面快速加载,并且生成的页面可以被搜索引擎SEO跟踪。

如果在服务器端已经渲染完成页面后,再调用ReactDOM.render()方法,React会保留它且只做事件处理,以便有非常良好的首次加载体验。

示例:React不推荐在客户端使用renderToString方法,为了操作方便,我们在下例中使用这个方法在客户端渲染一个ReactElement为HTML(renderToString.html):

var html = ReactDOMServer.renderToString(
  <div className="divider">
    <h2>itbilu.com</h2>
  </div>);

document.getElementById('example').innerHTML = html;


2.2 渲染为静态HTML:renderToStaticMarkup()
string renderToStaticMarkup(ReactElement element)
  • element,React元素
  • 返回值:HTML

该方法与renderToString方法类似,但这个方法不会生成额外的DOM特性,如:data-react-id等 React内部所使用的特性。当你想使用一个简单的静态页面生成器时这个方法非常有用,它会剥离额外的特性且会节省大量字节。


3. React.Children

React.Children是一个工具类,它提供了对组件不透明数据结构this.props.children的处理功能。

3.1 React.Children.map
array React.Children.map(object children, function fn [, object thisArg])

在每个直接子元素(children)上调用fn函数。如果 children 是一个内嵌的对象或者数组,它将被遍历(不会传入容器对象到fn中)。如果children 参数是 null 或者 undefined,那么返回 null 或者 undefined 而不是一个空对象。


3.2 React.Children.forEach
React.Children.forEach(object children, function fn [, object thisArg])

React.Children.map方法功能类似,但不返回数组。


3.3 React.Children.count
number React.Children.count(object children)

统计children中的子组件数,和传递给mapforEach回调函数的调用次数一致。


3.4 React.Children.only
number React.Children.only(object children)

返回children中仅有的子级。否则抛出异常。


3.5 React.Children.toArray
array React.Children.toArray(object children)

将不透明的子组件children转换为一个水平的数组。这在你操作子组件集合使用render方法时非常有用,特别是在你想在传递结果前重新排列或分隔this.props.children时。

<think>我们已知JSX会被Babel转换为React.createElement调用,生成ReactElement(一个JavaScript对象)。但用户现在想知道如何将这个ReactElement转换为真正的DOM节点。这个过程实际上是由React的渲染引擎(ReactDOM)完成的。根据引用[3]:JSX->React.createElement->JavaScript对象(ReactElement)->ReactDOM.render()->DOM元素->插入页面。所以,关键步骤是使用ReactDOM.render()方法ReactElement渲染到容器DOM节点中。具体步骤:1.创建ReactElement(可以通过JSX转换而来,也可以直接调用React.createElement创建)。2.准备一个容器DOM节点(例如一个div),这个节点将作为渲染的目标容器。3.调用ReactDOM.render(element,container)方法,将ReactElement渲染到容器节点中。注意:在React18中,推荐使用新的根API(createRoot)而不是ReactDOM.render。下面分版本说明:一、React17及之前版本:使用ReactDOM.render方法。二、React18版本:使用ReactDOM.createRoot创建一个根,然后调用render方法。示例代码:React17及之前:```jsximportReactfrom'react';importReactDOMfrom'react-dom';constelement=<h1>Hello,world</h1>;constcontainer=document.getElementById('root');ReactDOM.render(element,container);```React18:```jsximportReactfrom'react';importReactDOMfrom'react-dom/client';constelement=<h1>Hello,world</h1>;constcontainer=document.getElementById('root');//创建根constroot=ReactDOM.createRoot(container);//渲染root.render(element);```但是,用户可能想知道React内部是如何将ReactElement转换成DOM节点的。这个过程大致如下:1.React会构建一个Fiber树(虚拟DOM树),每个Fiber节点对应一个ReactElement。2.在渲染阶段(renderphase),React会根据Fiber节点的类型(type)创建对应的DOM节点。-对于宿主组件(如div、span等),创建对应的原生DOM节点。-对于自定义组件,则调用组件的render方法(或函数组件本身)得到子ReactElement,然后递归处理。3.在提交阶段(commitphase),React会将创建好的DOM节点插入到容器节点中(根据引用[4]中的描述,包括三个主要逻辑:生成DOM节点、插入子孙节点、处理props)。更详细的过程:-当调用ReactDOM.render(或根节点的render方法)时,React开始协调(reconciliation)过程。-从根Fiber开始,遍历整个组件树,为每个Fiber节点创建对应的DOM节点(如果是宿主组件)。-处理props:将Fiber节点上的props设置到对应的DOM节点上(例如className、style、事件监听等)。-对于子节点,递归执行上述过程,并将子DOM节点追加到父DOM节点中(注意:在协调阶段并不直接操作DOM,而是在提交阶段一次性完成)。-最后,在提交阶段将整个DOM树挂载到容器节点上。注意:React18引入了并发模式,渲染过程可能被中断和恢复,但最终生成DOM节点的步骤是类似的。如果用户想要手动将JSX字符串转换为DOM节点(不通过ReactDOM.render),这通常是不推荐的,因为这样会绕过React的协调和更新机制。但如果是出于特殊需求(例如在非React环境中使用),可以使用以下方法:1.使用BabelStandalone在浏览器中编译JSX字符串为React.createElement调用,然后执行得到ReactElement对象。2.然后,使用ReactDOMServer.renderToStaticMarkup将ReactElement渲染为HTML字符串。3.最后,使用innerHTML将HTML字符串插入到DOM中。示例:```javascript//注意:这种方法仅适用于静态内容(无事件处理等),因为innerHTML不会绑定事件。//步骤1:使用Babel编译JSX字符串constjsxString=`<div>HelloWorld</div>`;const{code}=Babel.transform(jsxString,{presets:['react']});//此时code是类似"React.createElement('div',null,'HelloWorld')"的字符串//步骤2:执行代码得到ReactElement(需要React环境)constelement=eval(code);//注意:实际中避免使用eval,这里仅作演示//步骤3:使用ReactDOMServer将element转换为HTML字符串consthtmlString=ReactDOMServer.renderToStaticMarkup(element);//步骤4:插入到DOM中document.getElementById('container').innerHTML=htmlString;```但是,这种方法无法处理事件和状态等。因此,在React应用中,我们始终推荐使用ReactDOM.render(或根节点的render方法)来将ReactElement转换为DOM节点并插入页面。总结:-正常React应用:使用ReactDOM.render(React17及之前)或ReactDOM.createRoot().render(React18)将ReactElement转换为DOM并插入页面。-内部过程:React通过Fiber树的协调和提交阶段,创建DOM节点并设置属性,最后挂载到容器上。相关问题:1.ReactFiber架构是如何优化渲染过程的?2.在React中如何避免不必要的DOM操作?3.React的事件系统是如何工作的?</think>### 如何将 JSX 转换为原生 DOM 节点 在 React 中,JSX 到原生 DOM 的转换过程分为两个关键阶段,整个过程由 ReactReactDOM 协作完成: #### 第一阶段:JSX → React Element(虚拟 DOM) 1. **Babel 编译 JSX**: ```jsx // JSX 代码 const element = <div className="container">Hello</div>; ``` ↓ Babel 转换为: ```javascript const element = React.createElement( 'div', { className: 'container' }, 'Hello' ); ``` - 输出结果是一个 **React Element 对象**(虚拟 DOM 节点)[^1][^2] - 对象结构示例: ```javascript { $$typeof: Symbol(react.element), type: 'div', props: { className: 'container', children: 'Hello' }, // ...其他元数据 } ``` #### 第二阶段:React Element → 原生 DOM 2. **ReactDOM 渲染**: ```javascript // 将虚拟 DOM 渲染为真实 DOM ReactDOM.render(element, document.getElementById('root')); ``` ReactDOM 内部执行流程: ```mermaid graph LR A[React Element] --> B[创建对应DOM节点] B --> C[设置DOM属性] C --> D[递归处理子元素] D --> E[插入DOM树] ``` 详细转换过程: 1. **DOM 节点创建**: - 根据 `type` 属性创建对应 HTML 元素(如 `div`) - 示例:`document.createElement('div')` 2. **属性处理**: - 将 JSX 属性转换为 DOM 属性: ```javascript // React 内部处理 if (className) domNode.className = className; if (style) Object.assign(domNode.style, style); ``` - 特殊属性处理(如 `onClick` 转为事件监听) 3. **子元素递归**: - 深度优先遍历 `children`: - 文本节点:`document.createTextNode('Hello')` - 嵌套元素:递归执行相同过程 - 生成完整的 DOM 子树 4. **挂载到容器**: - 最终调用:`container.appendChild(domNode)` - 将生成的 DOM 插入指定容器(如 `div#root`) #### 完整示例 ```jsx // 1. JSX 定义 const App = () => ( <div className="main"> <h1>标题</h1> <p>内容</p> </div> ); // 2. Babel 编译后(简化) const App = () => React.createElement( 'div', { className: 'main' }, React.createElement('h1', null, '标题'), React.createElement('p', null, '内容') ); // 3. ReactDOM 渲染 ReactDOM.render( React.createElement(App, null), document.getElementById('root') ); // 4. 最终生成的 DOM 结构 /* <div id="root"> <div class="main"> <h1>标题</h1> <p>内容</p> </div> </div> */ ``` #### 关键注意事项 1. **性能优化**: - React 使用虚拟 DOM 进行差异比较(Diffing),仅更新变化的部分[^4] - 避免直接操作 DOM 以利用 React 的优化机制 2. **动态更新**: - 状态变化时会重新生成虚拟 DOM - React 通过协调(Reconciliation)过程计算最小 DOM 操作 3. **服务端渲染**: - 使用 `ReactDOMServer.renderToString()` 可直接输出 HTML 字符串 - 客户端通过 `ReactDOM.hydrate()` 附加交互功能 [^1]: JSX 本质是 `React.createElement` 的语法糖 [^2]: Babel 完成 JSX 到 JavaScript 的转换 [^3]: React 通过虚拟 DOM 实现高效渲染 [^4]: 提交阶段完成 DOM 挂载和更新 --- ### 相关问题 1. React 虚拟 DOM 和真实 DOM 的差异比较算法如何工作? 2. 在哪些场景下直接操作 DOM 比使用 React 更高效? 3. React 18 的新渲染 API(createRoot)与传统 render 有何区别? 4. 如何在不使用 React 的情况下实现类似 JSX 的模板引擎?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值