为何HeroIcons采用React.createElement而非直接返回纯SVG?
我在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

