升级到 Material UI 5(使用 Next.js)后服务器端渲染中断

Zie*_*mdi 6 material-ui server-side-rendering next.js

我将服务器端渲染(SSR) 工作应用程序迁移到 MUI 版本 5。我遵循了官方程序,但是当我禁用 JavaScript 时,我收到了一个原始 HTML 页面(没有 CSS)。您可以在此处看到它(如果它已关闭,抱歉;我经常重新部署以进行测试)。

\n
    \n
  1. 运行 codemod(v5.0.0/preset-safe 和 link-underline-hover)
  2. \n
  3. 更改了 makeStyles 调用以使用tss-react
  4. \n
  5. 根据Next.js 的 MUI 示例修改了我的_app_document文件
  6. \n
\n

我启动了官方SSR Next.js 实现。它表明它也不起作用。

\n

禁用 JavaScript 的服务器端渲染

\n

有关更多详细信息,以下是我的项目中的关键文件:

\n

_app.js

\n
import * as React from \'react\';\nimport Head from \'next/head\';\nimport {ThemeProvider} from \'@mui/material/styles\';\nimport CssBaseline from \'@mui/material/CssBaseline\';\nimport {CacheProvider} from \'@emotion/react\';\nimport theme from \'../components/theme\';\nimport createEmotionCache from "../lib/createEmotionCache";\nimport {StyledEngineProvider} from \'@mui/material/styles\';\nimport {ApolloProvider} from "@apollo/client";\nimport {SessionProvider} from "next-auth/react"\nimport {appWithTranslation} from "next-i18next";\nimport {useApollo} from "../apollo/client";\n// Client-side cache, shared for the whole session of the user in the browser.\nconst clientSideEmotionCache = createEmotionCache();\n\n//followed example: https://github.com/mui/material-ui/tree/master/examples/nextjs\nfunction App(props) {\n    const {Component, emotionCache = clientSideEmotionCache, pageProps} = props;\n    const apolloClient = useApollo(pageProps)\n\n    return (\n        <CacheProvider value={emotionCache}>\n            <StyledEngineProvider injectFirst>\n                <ApolloProvider client={apolloClient}>\n                    <SessionProvider session={pageProps.session}>\n                        <Head>\n                            <meta name="viewport" content="initial-scale=1, width=device-width"/>\n\n                            <title>WeAlly</title>\n                            <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width"/>\n                            <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png"/>\n                            <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png"/>\n                            <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png"/>\n                            <link rel="manifest" href="/images/site.webmanifest"/>\n                        </Head>\n\n                        <ThemeProvider theme={theme}>\n                            {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}\n                            <CssBaseline/>\n                            <Component {...pageProps} />\n                        </ThemeProvider>\n                    </SessionProvider>\n                </ApolloProvider>\n            </StyledEngineProvider>\n        </CacheProvider>\n    );\n}\n\n\nexport default appWithTranslation(App);\n
Run Code Online (Sandbox Code Playgroud)\n

_document.js

\n
import * as React from \'react\';\nimport Document, { Html, Head, Main, NextScript } from \'next/document\';\nimport createEmotionServer from \'@emotion/server/create-instance\';\nimport theme from \'../components/theme\';\nimport createEmotionCache from "../lib/createEmotionCache";\n\nexport default class MyDocument extends Document {\n    render() {\n        return (\n            <Html lang="en">\n                <Head>\n                    {/* PWA primary color */}\n                    <meta name="theme-color" content={theme.palette.primary.main} />\n                    <link rel="shortcut icon" href="/static/favicon.ico" />\n                    <link\n                        rel="stylesheet"\n                        href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"\n                    />\n                    <link rel="preconnect" href="https://fonts.googleapis.com"/>\n                    <link rel="preconnect" href="https://fonts.gstatic.com"/>\n                    <link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;700&display=swap"\n                          rel="stylesheet"/>\n\n                    <link\n                        href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,600;1,400&display=swap"\n                        rel="stylesheet"/>\n\n                    <link rel="stylesheet"\n                          href="https://fonts.googleapis.com/icon?family=Material+Icons&display=swap"\n                          media="screen"/>\n                    {/* Inject MUI styles first to match with the prepend: true configuration. */}\n                    {this.props.emotionStyleTags}\n                </Head>\n                <body>\n                <Main />\n                <NextScript />\n                </body>\n            </Html>\n        );\n    }\n}\n\n// `getInitialProps` belongs to `_document` (instead of `_app`),\n// it\'s compatible with static-site generation (SSG).\nMyDocument.getInitialProps = async (ctx) => {\n    const originalRenderPage = ctx.renderPage;\n\n    // You can consider sharing the same emotion cache between all the SSR requests to speed up performance.\n    // However, be aware that it can have global side effects.\n    const cache = createEmotionCache();\n    const { extractCriticalToChunks } = createEmotionServer(cache);\n\n    ctx.renderPage = () =>\n        originalRenderPage({\n            enhanceApp: (App) =>\n                (function EnhanceApp(props) {\n                    // console.log( \'enhancing app with cache: \', cache )\n                    return <App emotionCache={cache} {...props} />;\n                }),\n        });\n\n    const initialProps = await Document.getInitialProps(ctx);\n    // This is important. It prevents emotion from rendering invalid HTML.\n    // See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153\n    const emotionStyles = extractCriticalToChunks(initialProps.html);\n    // console.log(\'emotion style count: \', emotionStyles.styles.length)\n    const emotionStyleTags = emotionStyles.styles.map((style) => (\n        <style\n            data-emotion={`${style.key} ${style.ids.join(\' \')}`}\n            key={style.key}\n            // eslint-disable-next-line react/no-danger\n            dangerouslySetInnerHTML={{ __html: style.css }}\n        />\n    ));\n\n    return {\n        ...initialProps,\n        emotionStyleTags,\n    };\n};\n\n**package.json**\n\n{\n  "name": "we-ally-org",\n  "version": "1.0.0",\n  "scripts": {\n    "dev": "next",\n    "back": "node ./server/starter.js",\n    "build": "next build",\n    "start": "next start"\n  },\n  "dependencies": {\n    "@apollo/client": "^3.4.17",\n    "@emotion/react": "^11.8.2",\n    "@emotion/styled": "^11.8.1",\n    "@google/maps": "^1.1.3",\n    "@hapi/iron": "6.0.0",\n    "@mui/icons-material": "^5.5.1",\n    "@mui/material": "^5.5.2",\n    "@mui/styles": "^5.5.1",\n    "@next-auth/mongodb-adapter": "^1.0.3",\n    "@next/bundle-analyzer": "^11.1.0",\n    "@prisma/client": "2.16.1",\n    "apollo-server-micro": "^3.5.0",\n    "axios": "^0.21.1",\n    "body-parser": "^1.19.0",\n    "classnames": "^2.3.1",\n    "cookie": "^0.4.1",\n    "cors": "^2.8.5",\n    "deepmerge": "4.2.2",\n    "ejs": "^3.1.6",\n    "express-graphql": "^0.12.0",\n    "express-jwt": "^6.0.0",\n    "express-session": "^1.17.2",\n    "google-map-react": "^2.1.9",\n    "graphql": "^15.5.1",\n    "graphql-tools": "^8.1.0",\n    "graphql-ws": "^5.4.0",\n    "http-proxy": "^1.18.1",\n    "image-type": "^4.1.0",\n    "jodit-react": "^1.1.1",\n    "jsonwebtoken": "^8.5.1",\n    "linkify-react": "^3.0.4",\n    "linkifyjs": "^3.0.5",\n    "lodash": "^4.17.21",\n    "micro": "^9.3.4",\n    "moment": "^2.29.1",\n    "mongodb": "^4.4.1",\n    "next": "12",\n    "next-auth": "^4.3.1",\n    "next-compose-plugins": "^2.2.1",\n    "next-i18next": "^8.5.1",\n    "node-fetch": "^3.0.0",\n    "passport": "^0.4.1",\n    "passport-facebook": "^3.0.0",\n    "pino": "^6.11.3",\n    "prop-types": "^15.6.2",\n    "react": "^17.0.2",\n    "react-dom": "^17.0.2",\n    "react-ga": "^3.3.0",\n    "react-image-gallery": "^1.2.7",\n    "react-moment": "^1.1.1",\n    "react-player": "^2.9.0",\n    "react-share": "^4.4.0",\n    "react-use": "^17.2.4",\n    "sanitize-html": "^2.4.0",\n    "subscriptions-transport-ws": "^0.9.19",\n    "tss-react": "^3.6.0"\n  },\n  "license": "MIT",\n  "devDependencies": {\n    "@babel/core": "^7.15.5",\n    "@emotion/server": "^11.4.0",\n    "babel-plugin-styled-components": "^2.0.6",\n    "eslint": "8.11.0",\n    "eslint-config-next": "12.1.0"\n  }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

此外,依赖库已损坏:react-image-gallery 显示不正确,但我仍然没有深入研究。

\n

Gui*_*eis 0

今天,我一直在研究同样的问题,并找到了解决方案。您应该从应用程序中删除您的StyledEngineProvider情绪缓存并将其修改为:

 import createCache from '@emotion/cache';

// prepend: true moves MUI styles to the top of the <head> so they're loaded first.
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
export const createEmotionCache = () =>
  createCache({ key: 'css', prepend: true });
Run Code Online (Sandbox Code Playgroud)

我之前不明白 StyledEngineProvider 在做什么,但它正在更改 CSS 顺序并将 MUI 样式添加到标头顶部,但如果您在缓存解决方案中添加 prepend=true ,您将不再需要它。请参阅此链接

  • 嗨 Guilherme,我已经激活了 prepred:true 选项。我想我们没有同样的问题。这是描述我的错误报告:https://github.com/mui/material-ui/issues/32051 (2认同)