Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 《React源码》笔记
  • JSX
东流
2026-01-15
目录

元素创建

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

作用: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. 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

核心特性:

  • $$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

# 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

作用:React 17+ 新的 JSX 转换函数,优化了性能。

区别:

  • 更简单的参数结构
  • 将 key 作为单独参数传递
  • 生产环境专用

转换示例:

// JSX
<Item key="1" id={id} />

// 转换为
jsxProd(Item, { id: id }, "1");
1
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

作用:克隆并扩展 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

# 关键设计模式

# 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. 安全的 $$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

# 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

# 总结

  1. createElement:传统创建方法,处理子元素灵活
  2. ReactElement:核心工厂函数,创建标准化元素对象
  3. jsxProd:优化后的生产环境创建函数
  4. clonedElement:元素克隆和扩展工具

这些函数共同构成了 React 虚拟 DOM 的基础,是 JSX 编译后的实际运行时代码。

编辑 (opens new window)
上次更新: 2026/01/15, 06:03:07
最近更新
01
React16与React17+的JSX转换差异
01-15
02
ice.js中Model数据初始化原理
12-10
03
Pinia中实现监听action的原理
11-28
更多文章>
Theme by Vdoing | Copyright © 2024-2026 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式