
React 并不直接处理 JSX,而是通过 Babel 将其编译为 React.createElement() 调用;当 {items} 传入 JSX 时,实际是将一个 React 元素数组赋值给 props.children,React 在协调(reconciliation)阶段会递归遍历该数组并逐个挂载子节点。
react 并不直接处理 jsx,而是通过 babel 将其编译为 `react.createelement()` 调用;当 `{items}` 传入 jsx 时,实际是将一个 react 元素数组赋值给 `props.children`,react 在协调(reconciliation)阶段会递归遍历该数组并逐个挂载子节点。
在 React 开发中,我们常看到如下写法:
const items = this.state.toDoList.map(item => <li key={item.id}>{item.text}</li>);
return (
<div>
<h1>My "To Do" List:</h1>
<ul>{items}</ul>
</div>
);表面上看,{items} 是一个 JavaScript 数组(如 [<li>…</li>, <li>…</li>]),而 JSX 语法似乎“允许”直接插入数组——这容易让人误以为 React 对数组做了特殊语法支持。但事实并非如此:JSX 本身不执行任何渲染逻辑,它只是语法糖;真正的处理发生在编译和运行时两个阶段。
✅ 编译阶段:JSX → React.createElement()
Babel 使用 @babel/preset-react 将 JSX 转译为标准 JavaScript。例如:
<ul>{items}</ul>会被编译为类似以下代码(简化版):
React.createElement('ul', { children: items });而 items 本身是由 map() 生成的 React 元素数组,每个 <li> 也被转译为:
React.createElement('li', { key: item.id }, item.text)因此,最终 items 是一个由纯 JS 对象(即 React 元素)组成的数组,每个对象形如:
{
type: 'li',
props: { children: 'Buy milk', key: '123' },
key: '123'
}✅ 运行阶段:React 如何处理 children 数组?
当 React.createElement('ul', { children: items }) 执行后,生成的 ul 元素对象为:
{
type: 'ul',
props: {
children: [
{ type: 'li', props: { ... }, key: '123' },
{ type: 'li', props: { ... }, key: '456' }
]
}
}React 的协调器(Reconciler)在 diff 和渲染过程中,原生支持 children 为数组类型。它会:
- 检测
props.children是否为数组; - 若是,则对每个子元素调用
mountIndeterminateComponent或updateElement,递归完成 DOM 创建或更新; - 同时利用
key属性进行高效复用与移动判断(无key时将触发低效的 index-based diff)。
⚠️ 注意:
key不是可选优化项,而是强制要求。React 依赖key区分列表项身份。若省略key(尤其用index作 key),在动态增删/排序时极易引发状态错位、输入框失焦、动画异常等问题。
❌ 常见误区澄清
-
不是自动展开(spread):
{items}≠{...items}。React 并未在编译时插入...;它保留数组结构,并由运行时逻辑主动遍历。 -
不是“隐式 toString()”:像
<p>{['a', 'b', 'c']}</p>能显示"abc",是因为 React 对children中的原始值数组(string/number)会自动.join('')—— 但这仅适用于非 React 元素的简单类型,不可用于组件列表(否则报错:“Objects are not valid as a React child”)。 -
不能直接嵌入普通数组:
const test = ['a', 'b', 'c']; return <p>{test}</p>;可运行,但这是 React 的兼容性兜底行为(内部调用Array.isArray(children) && children.map(String)),不推荐用于生产环境。正确做法始终是返回 React 元素。
✅ 最佳实践总结
| 场景 | 推荐写法 | 说明 |
|---|---|---|
| 列表渲染 | list.map((item, idx) => <item key="{item.id}"></item>) |
✅ 使用稳定唯一 key(优先 id,禁用 index) |
| 条件 + 列表混合 | {isLoading ? <spinner></spinner> : list.length > 0 ? list.map(...) : <empty></empty>} |
✅ 避免 && 用于条件分支([] && <comp></comp> 会渲染空数组长度) |
| 多根节点 |
<header></header><main></main><footer></footer>> 或 <fragment></fragment>
|
✅ 使用 Fragment()避免无意义 DOM 节点 |
简言之:{items} 的本质,是把已构造好的 React 元素数组,作为 children 属性传递给父元素;React 的设计使其天然支持数组型 children,并通过 key 驱动高效更新——这不是语法黑魔法,而是编译+运行时协同的设计结果。 理解这一机制,是写出可维护、高性能 React 列表渲染代码的基础。


















