Next.js集成Material UI 4页面刷新后样式丢失问题排查
Next.js + Material UI 4 首屏样式丢失、类名不匹配问题修复
问题原因
该问题本质是SSR阶段生成的MUI类名哈希与客户端水合阶段生成的类名不一致:服务端输出的带类名的DOM和样式,在客户端启动后因为类名对不上被判定为无效,导致首屏无样式,直到热更新触发客户端重新渲染才会生成正确样式,控制台抛出的Prop className did not match就是类名不一致的直接提示。
你当前的配置有两个核心遗漏:
- 没有统一服务端、客户端的JSS类名生成规则,两边计算出的类名哈希不同
- 没有固定JSS样式的注入锚点,服务端和客户端插入样式的位置不一致,导致样式优先级、类名匹配都出问题
修复方案
按以下步骤调整配置即可:
- 修正
_document.js配置,预留JSS注入锚点,确保服务端完整收集全组件树的MUI样式
import React from 'react'; import Document, { Html, Head, Main, NextScript } from 'next/document'; import { ServerStyleSheets } from '@material-ui/core/styles'; import theme from "@helper/theme/theme"; export default class MyDocument extends Document { render() { return ( <Html lang="en"> <Head> <meta name="theme-color" content={theme.palette.primary.main} /> {/* 固定JSS服务端样式注入锚点 */} <style id="jss-server-side" /> </Head> <body> <Main /> <NextScript /> </body> </Html> ); } } MyDocument.getInitialProps = async (ctx) => { const sheets = new ServerStyleSheets(); const originalRenderPage = ctx.renderPage; ctx.renderPage = () => originalRenderPage({ enhanceApp: (App) => (props) => sheets.collect(<App {...props} />), }); const initialProps = await Document.getInitialProps(ctx); return { ...initialProps, styles: [ ...React.Children.toArray(initialProps.styles), sheets.getStyleElement() ], }; };
- 修正
_app.js配置,引入StylesProvider统一客户端、服务端的类名生成规则
import '@assets/fonts/global.css'; import React from 'react'; import Layout from "@layout/Layout"; import Footer from "@layout/Footer/Footer"; import Header from "@layout/Header/Header"; // Material UI import CssBaseline from "@material-ui/core/CssBaseline"; import theme from "@helper/theme/theme"; import { ThemeProvider as MuiThemeProvider, createGenerateClassName, StylesProvider } from "@material-ui/styles"; // Redux import { wrapper } from "@store/store"; // 固定类名生成规则,保证服务端/客户端生成的类名哈希完全一致 const generateClassName = createGenerateClassName({ productionPrefix: 'jss', seed: 'mui-app' }); function MyApp({ Component, pageProps }) { React.useEffect(() => { // 水合完成后移除服务端注入的冗余CSS const jssStyles = document.querySelector('#jss-server-side'); if (jssStyles?.parentElement) { jssStyles.parentElement.removeChild(jssStyles); } }, []); return ( <StylesProvider generateClassName={generateClassName}> <MuiThemeProvider theme={theme}> <CssBaseline /> <Header /> <Layout> <Component {...pageProps} /> </Layout> <Footer /> </MuiThemeProvider> </StylesProvider> ); } export default wrapper.withRedux(MyApp);
- 检查主题配置:确保
@helper/theme/theme导出的是静态主题对象,不要在组件渲染函数内动态创建主题实例,否则每次渲染都会生成新的类名规则。 - 删除项目根目录下的
.next缓存文件夹,重启开发服务即可生效。
内容的提问来源于stack exchange,提问作者fahad shaikh
相关产品推荐
相关产品推荐

