You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Vue中带h()参数的Render Function迁移至React

React 实现 Vue 带参数 Render Function 图标组件的方案

在React中没有Vue那样的render(h, context)模式,但可以通过函数组件+JSX(或React.createElement)实现完全等价的逻辑。以下是对应你Vue代码的React实现思路和代码示例:

核心概念映射

先明确Vue与React的核心概念对应关系:

  • Vue的props → React组件的props参数
  • Vue的data.class → React的className属性
  • Vue的data.style → React的style属性
  • Vue的listeners → React中以on开头的事件props(如onClick)
  • Vue的h(tag, options) → React的<tag {...props} />(JSX语法)或React.createElement(tag, props)

分步实现代码

1. 基础组件结构(函数组件)

React函数组件直接接收props,子元素通过props.children获取:

import React from 'react';

const MyIcon = (props) => {
  const { item, first, last, xs, sm, md, lg, icon, spacing, cType, cColor, cSize, customSize, ...restProps } = props;
  
  // 处理排序相关类名(对应Vue的reorderingClasses)
  let reorderingClasses = '';
  if (item) {
    const reorderEntries = Object.entries({ first, last }).filter(([_, val]) => val);
    reorderingClasses = reorderEntries.map(([key, val]) => `${key}-${val}`).join(' ');
  }

  // 处理尺寸类名(对应Vue的sizeClasses)
  const sizeEntries = Object.entries({ xs, sm, md, lg }).filter(([_, val]) => val);
  const sizeClasses = sizeEntries.map(([key, val]) => `col-${key}${val !== 'auto' ? `-${val}` : ''}`).join(' ');

  // 获取图标名称(对应Vue的iconName逻辑)
  let iconName = icon;
  if (!iconName && props.children) {
    // 提取子元素中的文本(Vue中children.pop().text的等价逻辑)
    const childTexts = React.Children.toArray(props.children).filter(child => typeof child === 'string');
    if (childTexts.length > 0) {
      iconName = childTexts.pop().trim();
    }
  }

  // 构建基础className数组
  const baseClasses = [
    'my-icon',
    cType && `my-icon--${cType}`,
    cColor && `my-icon--${cColor}`,
    cSize && `my-icon--${cSize}`,
    spacing && 'my-icon--spacing',
    reorderingClasses, // 加入排序类名
    sizeClasses // 加入尺寸类名
  ].filter(Boolean); // 过滤掉falsy值

  // 合并外部传入的className
  if (restProps.className) {
    if (Array.isArray(restProps.className)) {
      baseClasses.push(...restProps.className);
    } else {
      baseClasses.push(restProps.className);
    }
  }

  // 构建style对象
  const baseStyle = {
    fontSize: customSize
  };
  const mergedStyle = { ...restProps.style, ...baseStyle }; // 外部style优先级更高(和Vue的Object.assign顺序一致)

  // 提取事件和其他props(过滤掉已解构的属性)
  const { className, style, ...otherProps } = restProps;

  // 渲染最终的<i>标签
  return (
    <i
      className={baseClasses.join(' ')}
      style={mergedStyle}
      {...otherProps} // 传递剩余props(包括事件)
    >
      {/* 如果需要显示图标名称,这里可以渲染iconName,根据你的业务逻辑调整 */}
      {iconName && iconName}
    </i>
  );
};

export default MyIcon;

关键逻辑说明

  • 类名处理:用数组收集所有类名,通过filter(Boolean)过滤无效值,最后join(' ')生成最终className,避免空字符串或undefined导致的样式问题。
  • 子元素文本提取:使用React.Children.toArray()统一处理子元素,过滤出文本类型的子元素,模拟Vue中children.pop().text的逻辑。
  • props合并:通过展开运算符...合并外部传入的props(包括事件),React中事件自动以on开头,直接传递即可。
  • style合并:用对象展开运算符合并外部style和内部style,保持和Vue中Object.assign(data.style, options.style)一致的优先级(外部style覆盖内部)。

可选优化建议

  • 可以使用classnames库简化类名拼接逻辑,比如classnames('my-icon', { 'my-icon--spacing': spacing }, reorderingClasses),减少手动数组处理代码。
  • 如果需要更接近Vue渲染函数的写法,也可以使用React.createElement替代JSX,示例:
    return React.createElement('i', {
      className: baseClasses.join(' '),
      style: mergedStyle,
      ...otherProps
    }, iconName);
    

内容的提问来源于stack exchange,提问作者learningMonk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 23:36:21