reactjs - 如何向 ctx.renderPage 添加多个样式表?
问题描述
我想在我的项目中使用 Material UI 和 styled-components。让两者中的一个工作不是问题,但是当我尝试同时添加它们时,它似乎不起作用。
在我的_document.ts
中,我像这样使用 Mui:
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => muiSheets.collect(<App {...props} />)
});
我尝试将样式组件添加到此设置中,如他们的示例中所示,
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => {
return {
...muiSheets.collect(<App {...props} />),
...styledComponentsSheet.collectStyles(<App {...props} />),
};
},
});
但我不确定如何合并这两者。是否只能使用一种样式?
这是我的全部_document.tsx
import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheets } from '@material-ui/core/styles';
import { ServerStyleSheet } from 'styled-components';
import theme from '../src/theme';
export default class MyDocument extends Document {
render() {
return (
<Html lang='en'>
<Head>
<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>
);
}
}
MyDocument.getInitialProps = async (ctx) => {
const muiSheets = new ServerStyleSheets();
const styledComponentsSheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => {
return {
...styledComponentsSheet.collectStyles(<App {...props} />),
};
},
});
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => {
return {
...muiSheets.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), muiSheets.getStyleElement(), styledComponentsSheet.getStyleElement()],
};
};
不过,我不确定这是否有效,因为我遇到了错误,例如:
Warning: Prop `className` did not match. Server: "sc-AxirZ hPlbpz" Client: "sc-AxjAm hhoYMA
而且我尝试过更改我的 babelrc,例如在一些 github 问题上提出的建议,包括这个问题,但无济于事。
如何正确合并两个样式表,在这种情况下是 Material UI 和 styled-components?
解决方案
以下设置将适用于 NextJS(请阅读下面的注释以获取更多信息)...
工作示例:
babel.config.js
module.exports = api => {
// cache babel configurations via NODE_ENV environments
// development, production, testing, staging...etc
api.cache(() => process.env.NODE_ENV);
return {
presets: ["next/babel"],
plugins: [
// leverage the 'babel-plugin-styled-component' plugin for...
// 1.) consistently hashed component classNames between environments (a must for server-side rendering)
// 2.) better debugging through automatic annotation of your styled components based on their context in the file system, etc.
// 3.) minification for styles and tagged template literals styled-components usages
[
"styled-components",
{
ssr: true,
displayName: true,
preprocess: false
}
],
]
};
};
页面/_documents.js
// import the Document (required), import HTML, Head, Main, NextScript (optional)
import Document, { Html, Head, Main, NextScript } from "next/document";
// import the SC 'ServerStyleSheet' (required)
import { ServerStyleSheet } from "styled-components";
// import the MUI 'ServerStyleSheets' (required)
import { ServerStyleSheets as MaterialUiServerStyleSheets } from "@material-ui/core/styles";
// import the package.json version (optional)
import { version } from "../../package.json";
class CustomDocument extends Document {
static async getInitialProps(ctx) {
// create SC sheet
const sheet = new ServerStyleSheet();
// create MUI sheets
const materialUISheets = new MaterialUiServerStyleSheets();
// keep a reference to original Next renderPage
const originalRenderPage = ctx.renderPage;
try {
// set the renderPage as a function that...
ctx.renderPage = () =>
// utilizes the original renderPage function that...
originalRenderPage({
// overrides the enhanceApp property with 2 returned wrapped fn's
enhanceApp: App => props =>
// that collects and returns SC + MUI styles from App
sheet.collectStyles(materialUISheets.collect(<App {...props} />))
});
// invoke internal Next getInitialProps (which executes the above)
const initialProps = await Document.getInitialProps(ctx);
return {
// from getInitialProps, spread out any initial props
...initialProps,
// and apply any initial style tags...
// and apply the MUI style tags...
// and apply the SC style tags...
// to the document's head:
// <html>
// <head>
// ...styles are placed here
// </head>
// <body>...</body>
// </html>
styles: (
<>
{initialProps.styles}
{materialUISheets.getStyleElement()}
{sheet.getStyleElement()}
</>
)
};
} finally {
// seal the SC sheet -- MUI sheets don't need to be sealed or do so internally
sheet.seal();
}
}
// below is completely optional...
// create a custom 'render' method for SEO tags
render() {
return (
<Html lang="en">
<Head>
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="NAME OF YOUR WEBSITE HERE" />
<meta name="theme-color" content="#000000" />
<meta name="msapplication-TileColor" content="#000000" />
<meta name="msapplication-navbutton-color" content="#000000" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />
<meta name="build version" content={version} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default CustomDocument;
页面/_app.js
// since we're using 'useEffect' import named exports as React (required)
import * as React from "react";
// import Head for client-side viewport SEO (optional and can be removed)
import Head from "next/head";
// if not using hooks, swap out 'useEffect' for 'componentDidMount'
// and destructure { Component, pageProps } from 'this.props'
// if you're not using 'getInitialProps' on the client-side then
// you don't need to extend Next's 'App' from 'next/app'
// instead just utilize a function that returns Component with pageProps (like below)
const App = ({ Component, pageProps }) => {
React.useEffect(() => {
// MUI has 2 sheets: 1 for server-side and 1 for client-side
// we don't want this duplication, so during initial client-side load,
// attempt to locate duplicated server-side MUI stylesheets...
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles && jssStyles.parentNode)
// ...and if they exist remove them from the head
jssStyles.parentNode.removeChild(jssStyles);
}, []);
return (
<React.Fragment>
<Head>
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
</Head>
<Component {...pageProps} />
</React.Fragment>
);
};
export default App;
推荐阅读
- c# - SmartBreadCrumbs - 使用父级别的路由值
- python - 在 HTML 文件上显示 Python 数据
- firebase - Flutter - 从其他页面的流构建器获取数据
- c# - 通过 WinApi32 显示气球弹出窗口
- javascript - 如何执行批量 Upsert
- c# - 如何找到孩子的父对象并启用真正的孩子?
- php - LARAVEL:像高级程序员一样设计模板的最佳实践是什么
- mongodb - 只有两个服务器实例的 MongoDB 复制和故障转移
- python - 如何矢量化以删除 Python 中的 for 循环?
- python - 将具有不同“年份”列的 Python 数据框转换为连续时间序列