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

为何HeroIcons采用React.createElement而非直接返回纯SVG?

关于HeroIcons使用React.createElement而非直接JSX的疑问

我在Next.js应用中使用HeroIcons时,对它的包架构有疑问。HeroIcons的图标组件是这样实现的:

const React = require("react");

function ArchiveIcon(props, svgRef) {
  return /*#__PURE__*/React.createElement("svg", Object.assign({
    xmlns: "http://www.w3.org/2000/svg",
    fill: "none",
    viewBox: "0 0 24 24",
    strokeWidth: 2,
    stroke: "currentColor",
    "aria-hidden": "true",
    ref: svgRef
  }, props), /*#__PURE__*/React.createElement("path", {
    strokeLinecap: "round",
    strokeLinejoin: "round",
    d: "M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
  }));
}

const ForwardRef = React.forwardRef(ArchiveIcon);
module.exports = ForwardRef;

但我想知道,为什么不能直接写成下面这种JSX形式,而非要用React.createElement创建DOM元素?

function ArchiveIcon({className}) {
  return <svg className={className} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
    <path
      fill="#000"
      fillRule="evenodd"
      d="M11.353 2H2v9.334L12.66 22 22 12.653 11.353 2zm-7.242 8.459V4.112h6.368l8.536 8.542-6.356 6.359-8.548-8.554zm4.096-2.252v-2.07h-2.07v2.07h2.07z"
      clipRule="evenodd"
    ></path>
  </svg>
};

核心原因解析

  • 兼容性更广:HeroIcons要适配所有React环境,包括没配置JSX编译的项目(比如纯Node环境、老版构建工具)。React.createElement是React原生API,无需Babel/TypeScript编译就能直接运行;而JSX必须经过编译转成React.createElement调用,直接写API能让包的适用范围更大。

  • props与ref的统一管控:官方代码用Object.assign合并默认props和用户传入的props,还通过forwardRef专门处理ref传递。用React.createElement能更直接地控制props合并逻辑,确保默认属性不会被用户属性意外覆盖,同时清晰管理ref转发——这对需要支持ref的组件来说很关键。

  • 构建优化更精准:代码里的/*#__PURE__*/注释是给Tree Shaking工具用的,告诉打包器这个函数调用无副作用,未被使用时可以安全剔除。虽然JSX编译后也会加这个注释,但直接写React.createElement能更精准地控制优化标识,确保图标组件能被正确摇树,减小包体积。

  • 批量生成更高效:HeroIcons的图标是脚本批量生成的,直接生成React.createElement代码比生成JSX更简单,不用处理JSX的语法细节(比如引号转义、标签闭合),脚本逻辑更简洁,还能保证所有图标组件的代码结构完全一致。

  • 样式与属性灵活性更强:官方例子用stroke: "currentColor"让图标颜色继承父元素文字色,而你写的例子硬编码了fill="#000",丢失了这种灵活性。用React.createElement合并props的方式,用户能轻松传入自定义的stroke、fill等属性覆盖默认值;如果JSX只处理className,就没法支持其他属性的自定义——当然JSX也能展开props,但官方写法是更标准的组件封装方式,确保所有props都能正确传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:30:44