Material UI:样式闪烁并消失

IT技术 reactjs material-ui next.js
2021-05-08 07:04:51

样式在此 SSR 应用程序的以下代码中出现可能 50 毫秒并消失。我很好奇是什么原因造成的。

// This component is a child of index.tsx in the /pages folder
    <Button
      color="primary"
      variant="outlined"
      size="large"
    >Test Button</Button>

样式消失后,会留下一个纯 HTML 按钮。

我相信 Next.js 是造成这种情况的原因。我检查了 Next.js 文件并将 next/babel 加载器添加到 .babelrc。除此之外,我只看到了其他相关的变化。这是在 /pages/_document.js 中:


MyDocument.getInitialProps = async ctx => {
  const sheets = new MuiServerStyleSheets();
  const originalRenderPage = ctx.renderPage;

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: App => props => sheets.collect(<App {...props} />),
    });

  const initialProps = await Document.getInitialProps(ctx);

  return {
    ...initialProps,
    // Styles fragment is rendered after the app and page rendering finish.
    styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
  };
};

试图解决的事情

  1. 重启服务器

没有更改发行。

  1. 强制刷新 Chrome 78 (CTRL + F5)

没变化。

理论

我认为存在服务器端问题。客户端和服务器应该在同一台机器上,localhost。这将解释正确的初始结果(客户端初始 UI)然后被服务器更新覆盖。

更新 1

忘了说我也更新/pages/_app.js了。这是更新的部分:

class MyApp extends App {
  componentDidMount() {
    // Remove the server-side injected CSS.
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles && "parentElement" in jssStyles) {
      (jssStyles.parentElement as HTMLElement).removeChild(jssStyles);
    }
  }
3个回答

对我来说,此错误的根本原因是在服务器端呈现文档期间生成的类与水化后生成的样式不匹配。

解决此问题的一种方法是在 hidration 后强制重新渲染。

一种方法是更新组件上的 key 属性。

// inside your component
const [key, setKey] = React.useState(0);

React.useEffect(() => {
  setKey(1);
}, []);

return (<MyComponent key={}key />)

我的完整_app.tsx文件:

import React from 'react';
import {
  ThemeProvider,
  createGenerateClassName,
  StylesProvider
} from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';

import { darkTheme } from '../theme';

const generateClassName = createGenerateClassName({
  productionPrefix: 'myclasses-'
});

export default function MyApp(props) {
  const { Component, pageProps } = props;

  const [key, setKey] = React.useState(0);

  React.useEffect(() => {
    setKey(1);
  }, []);

  React.useEffect(() => {
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);

  return (
    <StylesProvider key={key} generateClassName={generateClassName}>
      <React.Fragment>
        <ThemeProvider theme={darkTheme}>
          <CssBaseline />
          <Component {...pageProps} />
        </ThemeProvider>
      </React.Fragment>
    </StylesProvider>
  );
}

TLDR;如果您正在运行生产版本,请确保将 NODE_ENV 设置为生产,例如: NODE_ENV=production npm start


对我来说,这仅发生在我的机器上,当npm run build使用npm start.

奇怪的是,为什么服务器渲染的响应使用开发风格的类,makeStyles-root-123而不是https://material-ui.com/styles/advanced/#class-namesjss123所解释的

很明显,服务器的某些部分表现得就像我们仍处于开发环境中一样。为了解决这个问题,我尝试将生产版本NODE_ENV设置为“生产”,问题就消失了。

暂时通过注释掉去掉服务器的代码解决 jssStyles

class MyApp extends App {
  // componentDidMount() {
  //   // Remove the server-side injected CSS.
  //   const jssStyles = document.querySelector('#jss-server-side');
  //   if (jssStyles && "parentElement" in jssStyles) {
  //     (jssStyles.parentElement as HTMLElement).removeChild(jssStyles);
  //   }
  // }

如果有人知道为什么此代码包含在示例中,请发表评论。一定是有原因的。https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_app.js