如何将 Nextjs + styled-components 与 material-ui 集成

Posted

技术标签:

【中文标题】如何将 Nextjs + styled-components 与 material-ui 集成【英文标题】:How to integrate Nextjs + styled-components with material-ui 【发布时间】:2019-08-02 05:14:03 【问题描述】:

1. 使用样式化组件构建 next.js 应用程序非常简单。您只需要使用他们的_document.js sn-p 来启用 s-s-r 并防止样式在页面加载时闪烁:https://github.com/zeit/next.js/blob/canary/examples/with-styled-components/pages/_document.js

2. 使用 material-ui 构建 next.js 应用程序几乎一样简单。您只需从项目基础开始:https://github.com/mui-org/material-ui/tree/master/examples/nextjs,它在_document.js 上有自己的实现:https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_document.js

3. 遗憾的是,我不知道如何“合并”这两个实现并获得下一个应用程序,其中样式组件和材料 UI 组件可以共存,s-s-r 并且不闪烁在页面加载。

你能帮帮我吗? 互联网上是否有人比我的能力更好,已经解决了这个问题但我不知道?

提前致谢。

【问题讨论】:

在这里,您可以找到一个工作示例github.com/manakuro/nextjs-styled-component-material-ui-example 并发布关于它的帖子javascript.plainenglish.io/…。这是一种常见的方法,我也使用它。 这里你也可以找到一个很好的例子:link 【参考方案1】:

试试这个

_document.js

import React from 'react';
import Document,  Head, Main, NextScript  from 'next/document';
import  ServerStyleSheet  from 'styled-components'
import  ServerStyleSheets  from '@material-ui/styles';
import theme from '../src/theme';

class MyDocument extends Document 
  static async getInitialProps (ctx) 
    const styledComponentsSheet = new ServerStyleSheet()
    const materialSheets = new ServerStyleSheets()
    const originalRenderPage = ctx.renderPage;

    try 
        ctx.renderPage = () => originalRenderPage(
            enhanceApp: App => props => styledComponentsSheet.collectStyles(materialSheets.collect(<App ...props />))
          )
        const initialProps = await Document.getInitialProps(ctx)
        return 
          ...initialProps,
          styles: (
            <React.Fragment>
              initialProps.styles
              materialSheets.getStyleElement()
              styledComponentsSheet.getStyleElement()
            </React.Fragment>
          )
        
       finally 
        styledComponentsSheet.seal()
      
  

  render() 
    return (
      <html lang="en" dir="ltr">
        <Head>
          <meta charSet="utf-8" />
          /* Use minimum-scale=1 to enable GPU rasterization */
          <meta
            name="viewport"
            content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
          />
          /* PWA primary color */
          <meta
            name="theme-color"
            content=theme.palette.primary.main
          />
          <link
            rel="stylesheet"
            href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  


export default MyDocument;

.babelrc


  "presets": ["next/babel"],
  "plugins": [["styled-components",  "s-s-r": true ]]

更新检查https://github.com/nblthree/nextjs-with-material-ui-and-styled-components

【讨论】:

@alevardi 我在测试后更新了我的答案。现在它毫无疑问地起作用了。 @alevardi 这是我创建的一个 github 存储库github.com/MarchWorks/nextjs-with-material-ui-styled-components 非常感谢,你太棒了。奇迹般有效! :) @Alevardi 如果有任何过时的内容,请打开一个问题,我会查看它并感谢您的报告 实际上,我错了,也许当@MehmetN.Yarar 发现这个线程时它已经过时了,现在购买我刚刚克隆了你的仓库以查看发生了什么并且一切正常。非常感谢您的这项工作!【参考方案2】:

我遵循了这些示例方法并为我工作得很好:

https://github.com/mui-org/material-ui/tree/next/examples/nextjs-with-styled-components-typescript

【讨论】:

您的示例有效。但与此同时,静态构建存在问题link【参考方案3】:

对于仍然遇到上述代码问题的每个人:我还必须在 pages/_app.tsx 中包含 StylesProvider。

_app.tsx:

import  StylesProvider  from '@material-ui/core/styles';

<StylesProvider injectFirst>
  /* Your component tree.
      Now, you can override Material-UI's styles. */
</StylesProvider>

_document.tsx:

import React from 'react';
import Document,  Head, Main, NextScript  from 'next/document';
import  ServerStyleSheets  from '@material-ui/styles';
import  ServerStyleSheet  from 'styled-components';

class MyDocument extends Document 
  render() 
    return (
      <html lang="en">
        <Head>
          <meta charSet="utf-8" />
          <meta
            name="viewport"
            content="width=device-width, initial-scale=1, shrink-to-fit=no"
          />
          <meta name="theme-color" content="#000000" />
          /* Fonts and icons */
          <link
            rel="stylesheet"
            type="text/css"
            href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Roboto+Slab:400,700|Material+Icons"
          />
          <link
            href="https://use.fontawesome.com/releases/v5.0.10/css/all.css"
            rel="stylesheet"
          />
        </Head>
        <body>
          <div id="page-transition" />
          <Main />
          <NextScript />
        </body>
      </html>
    );
  


MyDocument.getInitialProps = async (ctx) => 

  // Render app and page and get the context of the page with collected side effects.
  const materialSheet = new ServerStyleSheets();
  const styledComponentSheet = new ServerStyleSheet();
  const originalRenderPage = ctx.renderPage;

  try 
    ctx.renderPage = () =>
      originalRenderPage(
        enhanceApp: (App) => (props) =>
          styledComponentSheet.collectStyles(
            materialSheet.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),
        materialSheet.getStyleElement(),
        styledComponentSheet.getStyleElement(),
      ],
    ;
   finally 
    styledComponentSheet.seal();
  
;

export default MyDocument;

【讨论】:

这对我有用,感觉很干净,谢谢【参考方案4】:

这是我的_document.js 文件的样子:

    import Document from "next/document";
    import  ServerStyleSheet  from "styled-components";

    export default class MyDocument extends Document 
      static async getInitialProps(ctx) 
      const sheet = new ServerStyleSheet();
      const originalRenderPage = ctx.renderPage;

      try 
        ctx.renderPage = () =>
          originalRenderPage(
            enhanceApp: App => props => sheet.collectStyles(<App ...props />)
          );
        const initialProps = await Document.getInitialProps(ctx);

        return 
          ...initialProps,
          styles: (
            <>
              initialProps.styles
              sheet.getStyleElement()
            </>
          )
        ;
       finally 
        sheet.seal();
      
    
  

在下面找到我的.babelrc 文件:

 
  "presets": ["next/babel"], 
  "plugins": [
      ["styled-components",  "s-s-r": true ]
   ]

【讨论】:

【参考方案5】:

此解决方案适用于服务器端,对于客户端,您还需要更改注入顺序,如下所述:https://material-ui.com/customization/css-in-js/#css-injection-order

要使用 next.js,您需要像这样更改 jss 常量的分配:

const jss = create(
  ...jssPreset(),
  insertionPoint: process.browser
    ? window.document.getElementById('jss-insertion-point')
    : null
)

【讨论】:

以上是关于如何将 Nextjs + styled-components 与 material-ui 集成的主要内容,如果未能解决你的问题,请参考以下文章

如何将 nextjs 应用程序置于维护模式(使用 Vercel)

如何将 Redux devtools 与 Nextjs 一起使用?

如何将道具从组件传递到 NextJS 中的 getServerSideProps()?

nextjs如何监听页面变化

如何通过nextjs将ssl集成到websocket中?

如何从 Nextjs 中的另一个页面将附加数据传递给 getserversideprops?