如何在Emotion的css字符串模板中使用props实现样式复用
Emotion 下复用带 Props 逻辑样式片段的方案
你不需要包装新组件就能实现和 styled-components 完全一致的样式复用效果,核心是调整可复用片段的写法,适配 Emotion 的样式插值规则。
核心原因说明
你原来的 SC 写法不兼容,本质是两个库的css方法实现逻辑不同:
- styled-components 的
css支持声明带 props 泛型的动态片段,插入 styled 模板时会自动透传组件 props - Emotion 原生的
css方法默认返回静态序列化样式对象,直接在css模板里写 props 插值不会自动绑定后续 styled 组件的上下文,所以拿不到属性值
最优方案:函数式样式片段(零改造、零额外依赖)
直接把可复用的样式片段写成「接收 props 为入参、返回 css 序列化样式」的函数,插入 styled 模板时 Emotion 会自动透传组件 props 给这个函数,和你原来的 SC 用法体验完全一致。
代码示例:
import { css, styled } from '@emotion/react'; // 你的原有类型定义保持不变 export interface AvatarProps { shape: AvatarShape; size: AvatarSize; } // 可复用片段:改成函数形式,入参是你需要的props字段,内部返回css包裹的样式 const borderRadiusByShape = (props: Pick<AvatarProps, "shape" | "size">) => css` border-radius: ${match(props.shape) .with("circle", () => `${props.size}px`) .with("square", () => "0px") .exhaustive()}; `; const StyledAvatar = styled.div<AvatarProps>` /* 你原来的其他静态/动态样式保持不变 */ width: ${props => `${props.size}px`}; /* 直接插入片段函数即可,props会自动透传 */ ${borderRadiusByShape}; `;
这个写法支持跨任意多个styled.[x]组件复用片段,类型推导正常,没有额外性能损耗,也不需要新增组件包装。
进阶方案:多片段批量合并(适合设计系统大规模复用场景)
如果你有大量类似的动态样式片段,可以写一个极简的合并工具函数,统一组合静态样式、动态片段,减少模板里的插值冗余:
import { css, SerializedStyles } from '@emotion/react'; // 通用样式合并工具 const combineStyles = <T extends object>( ...styleList: Array<SerializedStyles | ((props: T) => SerializedStyles)> ) => (componentProps: T) => css( ...styleList.map(styleItem => typeof styleItem === 'function' ? styleItem(componentProps) : styleItem ) ); // 多个可复用的动态样式片段 const borderRadiusByShape = (props: Pick<AvatarProps, "shape" | "size">) => css`/* 样式逻辑 */`; const colorByVariant = (props: Pick<AvatarProps, "variant">) => css`/* 样式逻辑 */`; // 使用 const StyledAvatar = styled.div<AvatarProps>( combineStyles( css`/* 组件基础静态样式 */`, borderRadiusByShape, colorByVariant ) );
注意:不要尝试给Emotion的
css方法手动传props来模拟SC的效果,这种写法脱离了styled的上下文绑定,动态更新时会出现样式不同步的问题,函数返回css的写法是Emotion官方推荐的动态样式复用模式。
内容的提问来源于stack exchange,提问作者Eliya Cohen
相关产品推荐
相关产品推荐

