缓存与性能 #

基本概念 #

Emotion 使用缓存机制来存储已生成的样式,避免重复计算。理解缓存机制和性能优化技巧,可以帮助你构建更高效的应用。

Emotion 缓存 #

默认缓存 #

Emotion 默认创建一个全局缓存:

jsx
import { css } from '@emotion/react'

function App() {
  return (
    <div css={css`color: red;`}>
      Hello
    </div>
  )
}

自定义缓存 #

使用 createCache 创建自定义缓存:

jsx
import { CacheProvider } from '@emotion/react'
import createCache from '@emotion/cache'

const myCache = createCache({
  key: 'my-app',
  prepend: true,
})

function App() {
  return (
    <CacheProvider value={myCache}>
      <Application />
    </CacheProvider>
  )
}

缓存配置选项 #

jsx
const cache = createCache({
  key: 'css',
  prepend: false,
  speedy: true,
  container: typeof document !== 'undefined' ? document.head : null,
  nonce: undefined,
  stylisPlugins: [],
  prefix: true,
})
选项 说明
key 生成的类名前缀
prepend 是否将样式插入到头部
speedy 使用快速模式(生产环境推荐)
container 样式插入的容器元素
nonce CSP nonce 值
stylisPlugins Stylis 插件数组

性能优化技巧 #

1. 提取样式定义 #

将样式提取到组件外部,避免每次渲染重新创建:

❌ 不推荐:

jsx
function Component() {
  const style = css`
    padding: 20px;
    background: #f5f5f5;
  `
  
  return <div css={style}>Content</div>
}

✅ 推荐:

jsx
const style = css`
  padding: 20px;
  background: #f5f5f5;
`

function Component() {
  return <div css={style}>Content</div>
}

2. 使用 useMemo 缓存动态样式 #

对于依赖 props 的动态样式,使用 useMemo 缓存:

jsx
/** @jsxImportSource @emotion/react */
import { css, useMemo } from '@emotion/react'

function Component({ size, color }) {
  const dynamicStyle = useMemo(() => css`
    font-size: ${size}px;
    color: ${color};
  `, [size, color])
  
  return <div css={dynamicStyle}>Content</div>
}

3. 避免内联对象 #

❌ 不推荐:每次渲染创建新对象

jsx
function Component() {
  return (
    <div css={{ padding: 20, margin: 10 }}>
      Content
    </div>
  )
}

✅ 推荐:使用稳定的样式引用

jsx
const style = { padding: 20, margin: 10 }

function Component() {
  return <div css={style}>Content</div>
}

4. 使用 styled 组件 #

对于大量重复的元素,styled 组件性能更好:

jsx
import styled from '@emotion/styled'

const Item = styled.li`
  padding: 8px;
  border-bottom: 1px solid #eee;
`

function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <Item key={item.id}>{item.name}</Item>
      ))}
    </ul>
  )
}

5. 减少样式计算 #

使用 CSS 变量减少动态样式计算:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const containerStyle = css`
  padding: var(--padding, 16px);
  background: var(--bg, #f5f5f5);
  color: var(--color, #333);
`

function Component({ padding, bg, color }) {
  return (
    <div
      css={containerStyle}
      style={{
        '--padding': `${padding}px`,
        '--bg': bg,
        '--color': color,
      }}
    >
      Content
    </div>
  )
}

样式复用 #

Mixin 模式 #

创建可复用的样式片段:

jsx
import { css } from '@emotion/react'

const mixins = {
  flexCenter: css`
    display: flex;
    align-items: center;
    justify-content: center;
  `,
  
  truncate: css`
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  `,
  
  cardShadow: css`
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  `,
  
  smoothTransition: css`
    transition: all 0.2s ease;
  `,
}

function Card({ children }) {
  return (
    <div css={[mixins.cardShadow, mixins.smoothTransition]}>
      {children}
    </div>
  )
}

样式函数 #

创建动态样式函数:

jsx
import { css } from '@emotion/react'

const getFlexStyles = (direction = 'row', justify = 'center', align = 'center') => css`
  display: flex;
  flex-direction: ${direction};
  justify-content: ${justify};
  align-items: ${align};
`

const getSpacingStyles = (padding = 16, margin = 0) => css`
  padding: ${padding}px;
  margin: ${margin}px;
`

选择器优化 #

避免深层嵌套 #

❌ 深层嵌套:

jsx
const Bad = styled.div`
  & > section {
    & > article {
      & > div {
        & > p {
          color: red;
        }
      }
    }
  }
`

✅ 扁平选择器:

jsx
const Good = styled.div`
  & p {
    color: red;
  }
`

使用组件选择器 #

jsx
import styled from '@emotion/styled'

const Icon = styled.span`
  margin-right: 8px;
`

const Button = styled.button`
  display: inline-flex;
  align-items: center;
  
  &:hover ${Icon} {
    transform: translateX(4px);
  }
`

动画性能 #

使用 transform 和 opacity #

优先使用 transformopacity 实现动画:

jsx
import styled from '@emotion/styled'

const AnimatedCard = styled.div`
  transition: transform 0.3s ease, opacity 0.3s ease;
  
  &:hover {
    transform: translateY(-4px);
    opacity: 0.9;
  }
`

will-change #

对于复杂动画,使用 will-change 提示浏览器:

jsx
const AnimatedElement = styled.div`
  will-change: transform, opacity;
  animation: ${keyframes} 1s ease;
`

contain #

使用 contain 属性优化渲染:

jsx
const Card = styled.div`
  contain: layout style paint;
`

包体积优化 #

按需导入 #

只导入需要的模块:

jsx
import styled from '@emotion/styled'
import { css } from '@emotion/react'

Tree Shaking #

确保构建工具支持 Tree Shaking:

json
{
  "sideEffects": false
}

代码分割 #

将样式组件按功能分割:

jsx
import lazy from '@emotion/styled'

const HeavyComponent = lazy(() => import('./HeavyComponent'))

性能监控 #

使用 React DevTools #

React DevTools 可以帮助你识别不必要的重渲染。

性能测量 #

jsx
import { Profiler } from 'react'

function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  commitTime
) {
  console.log(`${id} ${phase} took ${actualDuration}ms`)
}

function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <Application />
    </Profiler>
  )
}

缓存命中率 #

监控缓存命中率:

jsx
const cache = createCache({ key: 'css' })

function getCacheStats() {
  const inserted = Object.keys(cache.inserted).length
  const registered = Object.keys(cache.registered).length
  
  return { inserted, registered }
}

最佳实践总结 #

1. 样式定义 #

  • 将样式提取到组件外部
  • 使用 useMemo 缓存动态样式
  • 避免内联对象

2. 组件选择 #

  • 简单样式使用 css prop
  • 可复用组件使用 styled
  • 大量重复元素使用 styled

3. 动画优化 #

  • 使用 transformopacity
  • 使用 will-change 提示
  • 避免动画过多元素

4. 包体积 #

  • 按需导入
  • 支持 Tree Shaking
  • 代码分割

下一步 #

掌握了缓存与性能优化后,继续学习 项目结构,了解如何组织 Emotion 项目代码。

最后更新:2026-03-28