解决问题
- Next Server Component使用andt组件
- antd主题切换
- antd初次渲染样式未注入导致的闪烁问题
code
可以直接下载代码跑一下,然后直接看代码实现。
依赖
"@ant-design/nextjs-registry": "^1.0.1",
"ant-design": "^1.0.0",
"antd": "^5.21.1",
"cookies-next": "^4.2.1",
"next": "14.2.13",
"next-themes": "^0.3.0",
概述
防止样式闪烁
样式闪烁是因为antd的样式加载较慢,解决方式是提前在server端注入样式。
使用库:@ant-design/nextjs-registry,使用AntdRegistry包裹一下组件即可。
usage
import { AntdRegistry } from '@ant-design/nextjs-registry'
<AntdRegistry>{children</AntdRegistry>
主题切换
- 使用next-themes库实现主题切换
- 为了防止水合问题,需要把theme放在cookie里面,这样在server端可以取到
主题切换包括两部分,自定义的主题样式和UI库的主题样式。
自定义的主题样式使用CSS变量,根据不同的主题使用不同的变量。
亮色和暗色的变量定义如下:
:root {
--background: #ffffff;
--foreground: #171717;
}
[data-theme='dark'] {
--background: #0a0a0a;
--foreground: #ededed;
}
ant design v5提供了简单的方式设置主题,即修改ConfigProvider的theme属性
- defaultAlgorithm 亮色主题
- darkAlgorithm 暗色主题
import { ConfigProvider, theme } from 'antd'
function AntDesignProvider({ defaultTheme, children }: Props) {
const { defaultAlgorithm, darkAlgorithm } = theme
const { theme: currentTheme = defaultTheme } = useTheme()
return (
<ConfigProvider
theme={{
algorithm:
currentTheme === 'light' ? defaultAlgorithm : darkAlgorithm
}}
>
{children}
</ConfigProvider>
)
}
代码实现
App Router 主题切换的逻辑要写在layout.tsx中
- layout.tsx
-
这里需要注意我们首先从cookie中取主题,因为cookie在server端就可以拿到,保证antd不会因为在server端渲染拿不到theme出现水合错误
-
import type { Metadata } from 'next'
import './globals.css'
import { cookies } from 'next/headers'
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app'
}
import Providers from './Providers'
export default function RootLayout({
children
}: Readonly<{
children: React.ReactNode
}>) {
const cookieStore=cookies()
const defaultTheme=cookieStore.get('theme')?.value||'dark'
console.log('🐔🐔🐔',defaultTheme)
return (
<html lang="en" suppressHydrationWarning={true}>
<body>
<Providers defaultTheme={defaultTheme}>{children}</Providers>
</body>
</html>
)
}
- Providers.tsx
- 作用:切换主题库的样式
- 在server端,从cookie中取theme
- 在client端,从next-themes中取theme(其实是在localStrage中)
'use client'
import { ReactNode } from 'react'
import { ThemeProvider } from 'next-themes'
import { AntdRegistry } from '@ant-design/nextjs-registry'
import { ConfigProvider, theme } from 'antd'
import { useTheme } from 'next-themes'
interface Props {
defaultTheme: string
children: ReactNode
}
export default function Providers({ defaultTheme, children }: Props) {
return (
<AntdRegistry>
<ThemeProvider>
<AntDesignProvider defaultTheme={defaultTheme}>
{children}
</AntDesignProvider>
</ThemeProvider>
</AntdRegistry>
)
}
function AntDesignProvider({ defaultTheme, children }: Props) {
const { defaultAlgorithm, darkAlgorithm } = theme
const { theme: currentTheme = defaultTheme } = useTheme()
return (
<ConfigProvider
theme={{
algorithm:
currentTheme === 'light' ? defaultAlgorithm : darkAlgorithm
}}
>
{children}
</ConfigProvider>
)
}
- 主题切换组件 ThemeSwitch.tsx
- 注意该组件只能在客户端运行,因为next-themes基于localStrage,在server端取不到theme
'use client'
import { useState, useEffect } from 'react'
import { useTheme } from 'next-themes'
import { setCookie } from 'cookies-next'
const ThemeSwitch = () => {
const [mounted, setMounted] = useState(false)
const { theme, setTheme } = useTheme()
// useEffect only runs on the client, so now we can safely show the UI
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) {
return null
}
const handleSetTheme = (theme: string) => {
setTheme(theme)
setCookie('theme', theme)
}
return (
<select value={theme} onChange={(e) => handleSetTheme(e.target.value)}>
<option value="system">System</option>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
)
}
export default ThemeSwitch
注意事项
需要给html标签添加suppressHydrationWarning属性,不然控制台会有警告
<html lang="en" suppressHydrationWarning={true}>