优先用 styled 而非 css,因 styled 天然支持 props 驱动样式与自动缓存,而 css 返回类名字符串且不响应 props 变化;需避免对象引用不变导致样式不更新,嵌套伪类应使用 styled 模板字符串或 css 对象语法;ssr 条件样式需统一服务端与客户端判定逻辑以避免闪屏。

条件样式该用 css 还是 styled?
优先用 styled,除非你明确需要手动控制 className 或混用非 Emotion 组件。因为 css 函数返回的是类名字符串,它本身不感知 props 变化——你传进去的函数(如 props => props.active ? 'red' : 'gray')只在调用那一刻执行一次;后续 props 改变不会自动重算,得靠 useMemo 手动包裹,反而增加心智负担。
styled 组件天然支持 props 驱动样式,内部已做哈希缓存和引用比对,只要 props 是基础类型(string/number/boolean),就能正确响应变化。
- ✅ 正确:用
styled.button+ 模板字符串插值,如background-color: ${props => props.primary ? '#007bff' : '#6c757d'}; - ❌ 错误:在 render 里反复定义
const Button = styled.button`...`,每次都会注册新组件,破坏缓存 - ⚠️ 注意:
styled对象参数写法(styled.div({ color: props => props.error ? 'red' : 'black' }))也支持,但模板字符串更直观、兼容性更好
为什么 className 不变但样式没更新?
常见于用 styled 封装后 props 传入的是对象或函数引用未变,导致 Emotion 缓存命中旧规则。它只对「首次传入的 props」做一次计算,之后依赖浅比较判断是否需重新生成样式。
- 避免直接传
{ theme: { primary: '#007bff' } }这类对象——引用不变,样式就不更新 - 改用解构后的基础字段:
primaryColor={theme.primary},再在styled内部用${props => props.primaryColor} - 若必须基于复杂对象推导,先用
useMemo提取关键值,再透传,例如:const bgColor = useMemo(() => theme.colors.button, [theme.colors.button]) - 检查是否被
React.memo包裹——它可能拦截 props 更新,导致子组件收不到新值
嵌套伪类(如 &:hover)写在哪?
直接在 css 模板字符串里写 &:hover { color: red; } 在 Emotion v11+ 是无效的,会被当作文本忽略。这不是 bug,而是默认不解析嵌套语法。
- ✅ 正确做法一:用
styled组件,里面可直接写&:hover,例如:styled.div`&:hover { opacity: 0.8; }` - ✅ 正确做法二:在
css的对象语法中写,如css({ '&:hover': { opacity: 0.8 } }) - ⚠️ 注意:对象语法中伪元素要写全,
&::before不能简写为&:before,否则解析失败 - ? 不推荐强行启用
@emotion/babel-plugin——它在 monorepo(如 pnpm + changesets)下容易漏编译,且增加构建不确定性
SSR 下条件样式失效或闪屏?
不是样式没生效,而是服务端和客户端对“当前条件结果”认知不一致。比如服务端根据 User-Agent 判定为桌面端渲染了 isDesktop=true 的样式,客户端 hydration 时却按 window.innerWidth 判定为移动端,导致 DOM 结构 mismatch,样式被丢弃重绘。
- 服务端必须用统一 cache 实例,通过
CacheProvider注入,且客户端复用同一实例(不能new Cache()) - 条件逻辑尽量前置到服务端可确定的上下文,比如请求头、路由参数,而非依赖浏览器 API
- 若必须响应窗口尺寸,用
useEffect延迟到客户端执行,配合useState(false)+useLayoutEffect控制初始隐藏,避免 FOUC - 关键点:Emotion 自身不解决响应式断点一致性问题,需配合
fresnel或手动同步断点判定逻辑
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











