使用Next.js App Router实现自定义Material UI主题

50few1ms  于 2023-10-18  发布在  其他
关注(0)|答案(3)|浏览(163)

长期的C#开发,刚刚为客户端启动nextjs。
我使用MUI,基本上有一天的工作,所以我的项目很小。我有一个体面的处理如何SSR的工作原理概念,但实现是扔我一个奇怪的循环。
这是我的布局.js:

<!-- language:javascript -->
import "./globals.css";
import {
  Drawer,
  ListItemButton,
  ListItemIcon,
  ListItemText,
  List,
  AppBar,
  Toolbar,
  Typography,
  Box,
} from "@mui/material";
import AssessmentIcon from "@mui/icons-material/Assessment";
import AddIcon from "@mui/icons-material/Add";
import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted";
import zIndex from "@mui/material/styles/zIndex";
import { theme } from "./theme";
import { ThemeProvider } from "@mui/material/styles";

const navItems = [
  { name: "Create New", icon: <AddIcon />, url: "todo" },
  { name: "View all", icon: <FormatListBulletedIcon />, url: "todo" },
  { name: "Dashboard", icon: <AssessmentIcon />, url: "todo" },
];

const getNav = () => {
  console.log(navItems);
  return (
    <List>
      {navItems.map((item, index) => (
        <ListItemButton className="navbutton" href={item.url} key={index}>
          <ListItemIcon>{item.icon}</ListItemIcon>
          <ListItemText>{item.name}</ListItemText>
        </ListItemButton>
      ))}
    </List>
  );
};

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <ThemeProvider theme={theme}>
          <AppBar position="relative" sx={{ zIndex: zIndex.drawer + 1 }}>
            <Toolbar>
              <Typography varient="h4">
                --client info--
              </Typography>
            </Toolbar>{" "}
          </AppBar>
          <Drawer anchor={"left"} variant="permanent">
            {getNav()}
          </Drawer>
          <main>{children}</main>
        </ThemeProvider>
      </body>
    </html>
  );
}

和我友好的小主题.js:

<!-- language:javascript -->
import { createTheme } from "@mui/material/styles";

export default theme = createTheme({
  typography: {
    allVariants: {
      fontFamily: "Trebuchet MS",
    },
  },
});

我总是得到“**错误:尝试从服务器调用Theme(),但Theme在客户端上。无法从服务器调用客户端函数,它只能呈现为组件或传递给客户端组件的props。
我真的不明白为什么在这里。我已经尝试了一些变化的广告效果和“使用客户端”;.
据我所知...这个字体应该呈现服务器端?对吧?我觉得自己像是吃了疯狂的药。
Thanks in advance
试着用客户端语法,比较奇怪的错误,我觉得从根本上说这应该是服务器端渲染的,我不明白错误的含义。

huwehgph

huwehgph1#

服务器上发生的事情和客户端上发生的事情之间存在不匹配。现在使用Next.js App Router可能会非常混乱,即使对于我们这些精通使用Pages Router的旧方法的人来说也是如此。
看起来你需要设置一个主题注册表,以便在正确的时间在正确的位置调用各个部分。查看Material UI文档中的Next.js App Router guide,了解如何实现这一点。希望这对你有帮助!

sd2nnvve

sd2nnvve2#

正如已经提出的documentation中所述,您需要“指定”渲染发生在客户端。
尝试将'use client';添加到使用createTheme方法的文件的顶部

tf7tbtn2

tf7tbtn23#

我今天和你经历了同样的事情。我正在使用App Router和MUI组件库。

"@mui/system": "^5.4.1",    
"next": "^13.5.3",
  • 问题 *

让我们看看它在应用路由器增量采用指南中说了什么。
RootLayout布局是服务器组件,不能设置为客户端组件。
链接到文档
不能在服务器组件中使用上下文提供程序。
如果你使用的是任何React Context提供程序,它们需要移动到客户端组件。
链接到文档

  • 解决方案 *

通过我的设置,我可以将我的自定义字体应用到MUI系统中。
因为我使用的是应用路由器,RootLayout不能有一个hook Theme,这是一个改变基本MUI主题所必需的钩子。我在App Router根目录下创建了一个theme.js文件。

app/theme.js

此文件将全局css变量的名称分配给fontFamily属性。我还定制了几个MUI组件,以便它们在材质类型比例中使用不同的步骤。

const theme = createTheme({
  components: {
    MuiListItemText: {
      styleOverrides: {
        root: {
          "& .MuiTypography-root": {
            fontSize: '2rem',
          }
        },
      },
    },
    MuiList: {
      styleOverrides: {
        root: {
          "& .MuiMenuItem-root": {
            fontSize: '1.125rem',
          }
        },
      },
    },
  },
  typography: {
    h2: {
      fontSize: '4rem',
      fontFamily: 'var(--font-lavigne-display)',
      fontWeight: '300',
      fontStyle: 'normal'
    },
    h3: {
      fontSize: '2.5rem',
      fontFamily: 'var(--font-lavigne-display)',
      fontWeight: '300',
      fontStyle: 'normal'
    },
    h4: {
      fontSize: '2rem',
      fontFamily: 'var(--font-lavigne-display)',
      fontWeight: '300',
      fontStyle: 'normal'
    },
    h6: {
      fontSize: '1.125rem',
      fontFamily: 'var(--font-lavigne-display)',
      fontWeight: '400',
      fontStyle: 'normal'
    },
    body1: {
      fontSize: '1rem',
      fontFamily: 'var(--font-lavigne-text)',
      fontWeight: '400',
      fontStyle: 'normal',
      lineHeight: '1.2'
    },
  },
});

然后在我的RootLayout.js中,我将所有组件 Package 在ThemeProvider中,并将导入的主题传递给它。主题将fontFamily定义为全局变量名。因为这个变量被注入到文档中,所以fontFamily可用于所有呈现的元素。

app/layout.js

import { ThemeProvider } from '@mui/system';
import localFont from 'next/font/local';
import theme from './theme';

const lavigneDisplay = localFont({
  src: '../public/fonts/LavigneDisplayTRIAL-Light.ttf',
  weight: '400',
  style: 'normal',
  variable: '--font-lavigne-display',
})

const lavigneText = localFont({
  src: '../public/fonts/LavigneTextTRIAL-Regular.ttf',
  weight: '400',
  style: 'normal',
  variable: '--font-lavigne-text',
})

export default function RootLayout({ children }) {
  return (
      <html lang="en" className={`${lavigneDisplay.variable} ${lavigneText.variable}`}>
        <body>
          <ThemeProvider theme={theme}>
            {children}
          </ThemeProvider>
          <Analytics />
        </body>
      </html>
  )
}

相关问题