emotion性能瓶颈主要在css调用未缓存、ssr未提取样式、global/keyframes未清理三处;需用usememo缓存css函数、ssr时提取关键css并保持服务端客户端cache一致、global/keyframes手动管理生命周期。

Emotion 本身不慢,性能瓶颈几乎全出在 css 调用没缓存、SSR 时样式没提取、global/keyframes 长期驻留这三处。
css 函数必须用 useMemo 缓存,否则每次渲染都重复注入
React 组件内直接调用 css({ color: props.color }),哪怕参数完全一样,也会绕过缓存重新哈希、序列化、插入 <style></style> 标签——DevTools 里能看到一堆冗余的 .css-xxx 规则。
- 错误写法:
function Button({ primary }) { const s = css({ color: primary ? 'blue' : 'gray' }); return <button css="{s}"></button> } - 正确做法:把动态部分包进
useMemo,依赖项只含真正影响样式的变量:const dynamicStyle = useMemo(() => css({ color: primary ? 'blue' : 'gray' }), [primary]) - 更轻量的替代:简单值切换优先用 CSS 自定义属性,比如
style={{ '--text-color': props.color }}+ 静态css规则
SSR 必须提取关键 CSS,且服务端与客户端 cache 实例要一致
服务端没调用 renderStylesToString 或 extractCritical,客户端 hydration 时就会重跑所有 css 调用,不仅多执行逻辑,还可能反复增删 <style></style> 标签,引发 layout thrashing 和内存抖动。
- Node 端必须捕获样式:
const { css, html } = renderStylesToString(app),再把css插入 HTML 的 - 客户端 hydration 前需清空 Emotion 运行时缓存(仅 SSR 场景):
cache.reset(),否则服务端注入规则和客户端新规则可能冲突 - Next.js 用户注意:
@emotion/server版本必须与@emotion/react主版本一致,混用会导致缓存 key 错位,内存占用翻倍
styled 组件比 css 更适合响应 props,但别在 render 里定义
styled 内部已做 memoization,适合带交互、需响应 props 或主题的组件;但若在函数组件内部写 const StyledButton = styled.button(...),每次渲染都会注册新组件,缓存失效,反而更重。
- 正确姿势:定义在模块顶层,或用
useCallback包裹复杂插值逻辑 - 确保传给
styled的 props 是可序列化的基础类型(string、number、boolean),避免直接传obj或function - 嵌套伪类如
&:hover在css中无效,得用对象语法:css({ '&:hover': { color: 'red' } });styled则原生支持
global 和 keyframes 不会自动清理,必须手动管理生命周期
global 和 keyframes 创建的规则默认永不释放,微前端子应用卸载、主题切换后仍驻留在内存里,长期积累导致内存泄漏。
-
global推荐用useInsertionEffect+ 清理函数:useInsertionEffect(() => { const cleanup = insertGlobal(); return cleanup }, []) -
keyframes名称必须唯一且可预测,避免用随机 ID;否则每次生成新名称,缓存就多存一份 - SSR 场景下,多个请求共用一个
cache实例会导致样式互相污染,务必为每个请求创建独立实例
最常被忽略的是:服务端生成的样式 class 名,和客户端 hydration 时生成的 class 名必须一致——这取决于 cache 实例是否复用、@emotion/server 是否启用、以及 Babel 插件是否在服务端生效。差一个字符,FOUC 就来了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











