React元素创建介绍

createElementReactElement等是 React 元素创建系统的核心方法。让我详细解释每个函数的作用和实现逻辑:

1. createElement 函数

javascript 复制代码
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);
}

作用:React 传统的元素创建函数,通常用于 JSX 转换前。

转换示例

jsx 复制代码
// JSX
<Button type="submit" disabled>Click me</Button>

// 转换为
createElement(
  Button,
  { type: "submit", disabled: true },
  "Click me"
)

2. ReactElement 函数

javascript 复制代码
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;
}

核心特性

  • $$typeof: Symbol.for('react.element'):安全标记,防止 XSS
  • 这是虚拟 DOM 的基本结构
  • 不包含任何方法,只是数据描述对象

示例结果

javascript 复制代码
{
  $$typeof: Symbol(react.element),
  type: "div",
  key: null,
  ref: null,
  props: {
    className: "container",
    children: "Hello"
  }
}

3. jsxProd 函数

javascript 复制代码
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);
}

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

区别

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

转换示例

jsx 复制代码
// JSX
<Item key="1" id={id} />

// 转换为
jsxProd(Item, { id: id }, "1");

4. clonedElement 函数

javascript 复制代码
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;
}

作用:克隆并扩展 React 元素,常用于高阶组件。

使用场景

javascript 复制代码
// 添加新属性
const original = <Button primary>Click</Button>;
const cloned = clonedElement(original, { 
  disabled: true,
  onClick: handleClick 
});

// 相当于
<Button primary disabled onClick={handleClick}>Click</Button>

关键设计模式

1. 过滤特殊属性

javascript 复制代码
// 这些是 React 内部使用的属性
const specialProps = ['key', 'ref', '__self', '__source'];

// 在循环中过滤掉
if (propName !== 'key' && propName !== '__self' && propName !== '__source') {
    props[propName] = config[propName];
}

2. 安全的 $$typeof 标记

javascript 复制代码
$$typeof: Symbol.for('react.element')

为什么使用 Symbol

  • 防止服务器返回的恶意对象被当作 React 元素
  • 只有 React 运行时能创建真正的元素
  • 增强安全性,防止 XSS

3. 参数处理逻辑

javascript 复制代码
// createElement 支持变长参数
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
    props.children = children;
} else if (childrenLength > 1) {
    // 将多个子元素转为数组
}

4. 新旧 API 对比

特性 createElement jsxProd
参数结构 (type, props, ...children) (type, props, key)
key 位置 在 props 对象中 作为单独参数
JSX 转换 React 16 及之前 React 17+ 自动导入
性能 稍慢 更快

实际示例

创建元素

javascript 复制代码
// 使用 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. createElement:传统创建方法,处理子元素灵活
  2. ReactElement:核心工厂函数,创建标准化元素对象
  3. jsxProd:优化后的生产环境创建函数
  4. clonedElement:元素克隆和扩展工具

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

相关推荐
济6172 小时前
linux 系统移植(第六期)--Uboot移植(5)--bootcmd 和 bootargs 环境变量-- Ubuntu20.04
java·前端·javascript
lili-felicity2 小时前
React Native for OpenHarmony 实战:Easing 动画完全指南
javascript·react native·react.js
m0_748254662 小时前
AJAX 基础实例
前端·ajax·okhttp
vmiao2 小时前
【前端入门】商品页放大镜效果(仅放大镜随鼠标移动效果)
前端
持续前行2 小时前
vscode 中找settings.json 配置
前端·javascript·vue.js
Anita_Sun2 小时前
Lodash 源码解读与原理分析 - Lodash IIFE 与兼容性处理详解
前端
用户904706683572 小时前
Nuxt 请求后端接口怎么写,一篇文章讲清楚
前端
ahubbub2 小时前
用 maptalks 在 Web 上做可扩展的 2D/3D 地图渲染与交互
前端
JosieBook2 小时前
【Vue】11 Vue技术——Vue 中的事件处理详解
前端·javascript·vue.js