元素创建
createElement、ReactElement等是 React 元素创建系统的核心方法。让我详细解释每个函数的作用和实现逻辑
# 1. createElement 函数
export function createElement(type, config, children) {
// 创建 props 对象
const props = {};
let key = null;
// 处理 config 参数
if (config != null) {
// 提取 key(React 的特殊属性)
if (config.key !== undefined) {
key = '' + config.key; // 强制转换为字符串
}
// 复制除特殊属性外的所有属性到 props
for (propName in config) {
if (hasOwnProperty.call(config, propName) &&
propName !== 'key' &&
propName !== '__self' &&
propName !== '__source') {
props[propName] = config[propName];
}
}
}
// 处理子元素
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children; // 单个子元素
} else if (childrenLength > 1) {
// 多个子元素转为数组
const childArray = new Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// 创建并返回 React 元素
return ReactElement(type, key, undefined, undefined, null, props, undefined, undefined);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
作用:React 传统的元素创建函数,通常用于 JSX 转换前。
转换示例:
// JSX
<Button type="submit" disabled>Click me</Button>
// 转换为
createElement(
Button,
{ type: "submit", disabled: true },
"Click me"
)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2. ReactElement 函数
export function ReactElement(type, key, self, source, owner, props, debugStack, debugTask) {
const refProp = props.ref;
const ref = refProp !== undefined ? refProp : null;
// 创建 React 元素对象
let element = {
// 核心:Symbol 标识,防止 XSS
$$typeof: Symbol.for('react.element'),
type: type, // 组件类型(字符串或函数)
key: key, // 用于列表优化的唯一标识
ref: ref, // 引用 DOM 或组件实例
props: props, // 属性对象
}
return element;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
核心特性:
$$typeof: Symbol.for('react.element'):安全标记,防止 XSS- 这是虚拟 DOM 的基本结构
- 不包含任何方法,只是数据描述对象
示例结果:
{
$$typeof: Symbol(react.element),
type: "div",
key: null,
ref: null,
props: {
className: "container",
children: "Hello"
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 3. jsxProd 函数
export function jsxProd(type, config, maybeKey) {
let key = null;
// 处理 key
if (maybeKey !== undefined) {
key = '' + maybeKey; // 从单独参数获取
}
if (config.key !== undefined) {
key = '' + config.key; // 从 config 获取(优先级高)
}
// 分离 key 和其他 props
let props;
if (!('key' in config)) {
props = config; // config 中没有 key,直接使用
} else {
props = {};
for (const propName in config) {
if (propName !== 'key') {
props[propName] = config[propName];
}
}
}
return ReactElement(type, key, undefined, undefined, null, props, undefined, undefined);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
作用:React 17+ 新的 JSX 转换函数,优化了性能。
区别:
- 更简单的参数结构
- 将
key作为单独参数传递 - 生产环境专用
转换示例:
// JSX
<Item key="1" id={id} />
// 转换为
jsxProd(Item, { id: id }, "1");
1
2
3
4
5
2
3
4
5
# 4. clonedElement 函数
export function clonedElement(element, config, children) {
// 验证输入
if (element === null || element === undefined) {
throw new Error('React.cloneElement(...): The argument must be a React element');
}
// 复制原 props
const props = Object.assign({}, element.props);
let key = element.key;
let owner = undefined;
// 处理新的 config
if (config != null) {
// 处理 ref
if (config.ref != undefined) {
owner = undefined; // 重置 owner
}
// 更新 key
if (config.key !== undefined) {
key = '' + config.key;
}
// 合并 props
for (propName in config) {
if (hasOwnProperty.call(config, propName) &&
propName !== 'key' &&
propName !== '__source' &&
propName !== '__self' &&
!(propName === 'ref' && config.ref === undefined)) {
props[propName] = config[propName];
}
}
}
// 创建克隆元素
const clonedElement = ReactElement(
element.type,
key,
undefined,
undefined,
owner,
props,
undefined,
undefined,
)
return clonedElement;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
作用:克隆并扩展 React 元素,常用于高阶组件。
使用场景:
// 添加新属性
const original = <Button primary>Click</Button>;
const cloned = clonedElement(original, {
disabled: true,
onClick: handleClick
});
// 相当于
<Button primary disabled onClick={handleClick}>Click</Button>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 关键设计模式
# 1. 过滤特殊属性
// 这些是 React 内部使用的属性
const specialProps = ['key', 'ref', '__self', '__source'];
// 在循环中过滤掉
if (propName !== 'key' && propName !== '__self' && propName !== '__source') {
props[propName] = config[propName];
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 2. 安全的 $$typeof 标记
$$typeof: Symbol.for('react.element')
1
为什么使用 Symbol:
- 防止服务器返回的恶意对象被当作 React 元素
- 只有 React 运行时能创建真正的元素
- 增强安全性,防止 XSS
# 3. 参数处理逻辑
// createElement 支持变长参数
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
// 将多个子元素转为数组
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 4. 新旧 API 对比
| 特性 | createElement | jsxProd |
|---|---|---|
| 参数结构 | (type, props, ...children) | (type, props, key) |
| key 位置 | 在 props 对象中 | 作为单独参数 |
| JSX 转换 | React 16 及之前 | React 17+ 自动导入 |
| 性能 | 稍慢 | 更快 |
# 实际示例
# 创建元素
// 使用 createElement
const element1 = createElement(
'div',
{ className: 'container' },
createElement('span', null, 'Hello'),
createElement('span', null, 'World')
);
// 使用 jsxProd
const element2 = jsxProd(
'div',
{ className: 'container' },
null
);
// 克隆元素
const button = createElement('button', { type: 'button' }, 'Save');
const clonedButton = clonedElement(button, { disabled: true });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 总结
createElement:传统创建方法,处理子元素灵活ReactElement:核心工厂函数,创建标准化元素对象jsxProd:优化后的生产环境创建函数clonedElement:元素克隆和扩展工具
这些函数共同构成了 React 虚拟 DOM 的基础,是 JSX 编译后的实际运行时代码。
编辑 (opens new window)
上次更新: 2026/01/15, 06:03:07