gpt4 book ai didi

reactjs - 如何在NextJs中为Material UI的媒体查询实现SSR?

转载 作者:行者123 更新时间:2023-12-03 13:36:50 24 4
gpt4 key购买 nike

我无法关注 documentation实现 Material UI 的媒体查询,因为它是为普通的 React 应用程序指定的,而我正在使用 NextJs。具体来说,我不知道将文档指定的以下代码放在哪里:

import ReactDOMServer from 'react-dom/server';
import parser from 'ua-parser-js';
import mediaQuery from 'css-mediaquery';
import { ThemeProvider } from '@material-ui/core/styles';

function handleRender(req, res) {
const deviceType = parser(req.headers['user-agent']).device.type || 'desktop';
const ssrMatchMedia = query => ({
matches: mediaQuery.match(query, {
// The estimated CSS width of the browser.
width: deviceType === 'mobile' ? '0px' : '1024px',
}),
});

const html = ReactDOMServer.renderToString(
<ThemeProvider
theme={{
props: {
// Change the default options of useMediaQuery
MuiUseMediaQuery: { ssrMatchMedia },
},
}}
>
<App />
</ThemeProvider>,
);

// …
}

我想实现这个的原因是因为我使用媒体查询来有条件地渲染某些组件,如下所示:

const xs = useMediaQuery(theme.breakpoints.down('sm'))
...
return(
{xs ?
<p>Small device</p>
:
<p>Regular size device</p>
}
)

我知道我可以使用 Material UI 的 Hidden 但我喜欢这种方法,其中媒体查询是具有状态的变量,因为我还使用它们有条件地应用 CSS。

我已经在 SRR 中使用样式化组件和 Material UI 的样式。这是我的 _app.js

  import NextApp from 'next/app'
import React from 'react'
import { ThemeProvider } from 'styled-components'

const theme = {
primary: '#4285F4'
}

export default class App extends NextApp {
componentDidMount() {
const jssStyles = document.querySelector('#jss-server-side')
if (jssStyles && jssStyles.parentNode)
jssStyles.parentNode.removeChild(jssStyles)
}

render() {
const { Component, pageProps } = this.props

return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
<style jsx global>
{`
body {
margin: 0;
}
.tui-toolbar-icons {
background: url(${require('~/public/tui-editor-icons.png')});
background-size: 218px 188px;
display: inline-block;
}
`}
</style>
</ThemeProvider>
)
}
}

这是我的_document.js

import React from 'react'
import { Html, Head, Main, NextScript } from 'next/document'

import NextDocument from 'next/document'

import { ServerStyleSheet as StyledComponentSheets } from 'styled-components'
import { ServerStyleSheets as MaterialUiServerStyleSheets } from '@material-ui/styles'

export default class Document extends NextDocument {
static async getInitialProps(ctx) {
const styledComponentSheet = new StyledComponentSheets()
const materialUiSheets = new MaterialUiServerStyleSheets()
const originalRenderPage = ctx.renderPage

try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props =>
styledComponentSheet.collectStyles(
materialUiSheets.collect(<App {...props} />)
)
})

const initialProps = await NextDocument.getInitialProps(ctx)

return {
...initialProps,
styles: [
<React.Fragment key="styles">
{initialProps.styles}
{materialUiSheets.getStyleElement()}
{styledComponentSheet.getStyleElement()}
</React.Fragment>
]
}
} finally {
styledComponentSheet.seal()
}
}

render() {
return (
<Html lang="es">
<Head>
<link
href="https://fonts.googleapis.com/css?family=Comfortaa|Open+Sans&display=swap"
rel="stylesheet"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}

最佳答案

首先需要注意的是——我目前没有任何使用 SSR 的经验,但我有 deep knowledge of Material-UI我认为通过您问题中包含的代码和 Next.js 文档,我可以帮助您解决此问题。

您已经在_app.js中展示了如何将主题设置到样式组件ThemeProvider中。您还需要为 Material-UI ThemeProvider 设置主题,并且需要根据设备类型在两个可能的主题之间进行选择。

首先定义您关心的两个主题。这两个主题将使用 ssrMatchMedia 的不同实现 - 一种用于移动设备,一种用于桌面设备。

import mediaQuery from 'css-mediaquery';
import { createMuiTheme } from "@material-ui/core/styles";

const mobileSsrMatchMedia = query => ({
matches: mediaQuery.match(query, {
// The estimated CSS width of the browser.
width: "0px"
})
});
const desktopSsrMatchMedia = query => ({
matches: mediaQuery.match(query, {
// The estimated CSS width of the browser.
width: "1024px"
})
});

const mobileMuiTheme = createMuiTheme({
props: {
// Change the default options of useMediaQuery
MuiUseMediaQuery: { ssrMatchMedia: mobileSsrMatchMedia }
}
});
const desktopMuiTheme = createMuiTheme({
props: {
// Change the default options of useMediaQuery
MuiUseMediaQuery: { ssrMatchMedia: desktopSsrMatchMedia }
}
});

为了在两个主题之间进行选择,您需要利用请求中的用户代理。这是我的知识很浅的地方,所以我的代码可能存在一些小问题。我认为您需要使用 getInitialProps (或 Next.js 9.3 或更高版本中的 getServerSideProps)。 getInitialProps 接收 context object您可以从中获取 HTTP 请求对象 (req)。然后,您可以按照与 Material-UI 文档示例相同的方式使用 req 来确定设备类型。

下面是我认为 _app.js 应该是什么样子的近似值(未执行,因此可能存在轻微的语法问题,并且在 getInitialProps 中有一些猜测,因为我从未使用过 Next.js):

import NextApp from "next/app";
import React from "react";
import { ThemeProvider } from "styled-components";
import { createMuiTheme, MuiThemeProvider } from "@material-ui/core/styles";
import mediaQuery from "css-mediaquery";
import parser from "ua-parser-js";

const theme = {
primary: "#4285F4"
};

const mobileSsrMatchMedia = query => ({
matches: mediaQuery.match(query, {
// The estimated CSS width of the browser.
width: "0px"
})
});
const desktopSsrMatchMedia = query => ({
matches: mediaQuery.match(query, {
// The estimated CSS width of the browser.
width: "1024px"
})
});

const mobileMuiTheme = createMuiTheme({
props: {
// Change the default options of useMediaQuery
MuiUseMediaQuery: { ssrMatchMedia: mobileSsrMatchMedia }
}
});
const desktopMuiTheme = createMuiTheme({
props: {
// Change the default options of useMediaQuery
MuiUseMediaQuery: { ssrMatchMedia: desktopSsrMatchMedia }
}
});

export default class App extends NextApp {
static async getInitialProps(ctx) {
// I'm guessing on this line based on your _document.js example
const initialProps = await NextApp.getInitialProps(ctx);
// OP's edit: The ctx that we really want is inside the function parameter "ctx"
const deviceType =
parser(ctx.ctx.req.headers["user-agent"]).device.type || "desktop";
// I'm guessing on the pageProps key here based on a couple examples
return { pageProps: { ...initialProps, deviceType } };
}
componentDidMount() {
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles && jssStyles.parentNode)
jssStyles.parentNode.removeChild(jssStyles);
}

render() {
const { Component, pageProps } = this.props;

return (
<MuiThemeProvider
theme={
pageProps.deviceType === "mobile" ? mobileMuiTheme : desktopMuiTheme
}
>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
<style jsx global>
{`
body {
margin: 0;
}
.tui-toolbar-icons {
background: url(${require("~/public/tui-editor-icons.png")});
background-size: 218px 188px;
display: inline-block;
}
`}
</style>
</ThemeProvider>
</MuiThemeProvider>
);
}
}

关于reactjs - 如何在NextJs中为Material UI的媒体查询实现SSR?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61510890/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com