跳到主要内容

如何使用 CSS-in-JS 库

警告: 在使用 CSS-in-JS 结合 React 新特性(如服务端组件和流式渲染)时,库的作者需要支持最新版本的 React,包括 并发渲染

以下库已在 app 目录下的客户端组件中获得支持(按字母顺序排列):

以下库正在适配中:

温馨提示: 我们正在测试不同的 CSS-in-JS 库,并会为支持 React 18 特性和/或 app 目录的库补充更多示例。

app 目录中配置 CSS-in-JS

配置 CSS-in-JS 需要经过三步 opt-in 流程:

  1. 样式注册器:用于在一次渲染中收集所有 CSS 规则。
  2. 使用新的 useServerInsertedHTML hook,在任何可能用到样式的内容之前注入规则。
  3. 在服务端渲染初始阶段,用客户端组件包裹你的应用,并挂载样式注册器。

styled-jsx

在客户端组件中使用 styled-jsx 需要 v5.1.0 及以上版本。首先,创建一个新的注册器:

app/registry.tsx
'use client'

import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'

export default function StyledJsxRegistry({
children,
}: {
children: React.ReactNode
}) {
// 只在初始时懒加载创建样式表
// 参考:https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [jsxStyleRegistry] = useState(() => createStyleRegistry())

useServerInsertedHTML(() => {
const styles = jsxStyleRegistry.styles()
jsxStyleRegistry.flush()
return <>{styles}</>
})

return <StyleRegistry registry={jsxStyleRegistry}>{children}</StyleRegistry>
}

然后,用注册器包裹你的根布局

app/layout.tsx
import StyledJsxRegistry from './registry'

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<StyledJsxRegistry>{children}</StyledJsxRegistry>
</body>
</html>
)
}

查看示例

Styled Components

下面是如何配置 styled-components@6 或更高版本的示例:

首先,在 next.config.js 中启用 styled-components。

next.config.js
module.exports = {
compiler: {
styledComponents: true,
},
}

然后,使用 styled-components API 创建一个全局注册组件,用于收集渲染期间生成的所有 CSS 样式规则,并提供一个函数返回这些规则。再用 useServerInsertedHTML hook 将注册器收集到的样式注入到根布局的 <head> 标签中。

lib/registry.tsx
'use client'

import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

export default function StyledComponentsRegistry({
children,
}: {
children: React.ReactNode
}) {
// 只在初始时懒加载创建样式表
// 参考:https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())

useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement()
styledComponentsStyleSheet.instance.clearTag()
return <>{styles}</>
})

if (typeof window !== 'undefined') return <>{children}</>

return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
{children}
</StyleSheetManager>
)
}

用样式注册组件包裹根布局的 children

app/layout.tsx
import StyledComponentsRegistry from './lib/registry'

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
)
}

查看示例

温馨提示:

  • 服务端渲染时,样式会被提取到全局注册器,并刷新到 HTML 的 <head>。这样可以确保样式规则在任何内容使用前就已插入。未来我们可能会用 React 新特性来决定样式注入位置。
  • 流式渲染时,每个分块的样式会被收集并追加到已有样式中。客户端水合完成后,styled-components 会像往常一样接管并注入后续动态样式。
  • 我们特意在树的顶层用客户端组件作为样式注册器,因为这样提取 CSS 规则更高效。它避免了服务端多次渲染时重复生成样式,也防止样式被包含在服务端组件的 payload 中。
  • 如需配置 styled-components 编译的高级属性,请参阅 Next.js styled-components API 参考