reactjs 带有实验性appDir的Next.js 13未在< meta>< title>< head>服务器上的节中呈现或标记

8hhllhi2  于 2023-02-08  发布在  React
关注(0)|答案(2)|浏览(204)

我正在试用Next.js13的新experimental appDir,我遇到了一个小问题。
本项目基于:

  • 下一步. js 13
  • React18
  • MUI 5(样式化构件,使用@mui/system @emotion/react @emotion/styled

为了让MUI/Emotion在服务器端渲染中表现良好,有一个处理SSR缓存的开放票证,这意味着我必须将layout.tsx转换为一个仅面向客户端的组件,这意味着像<head>这样的组件现在只能在服务器端渲染。
为了解决这个问题,我将布局拆分为带有客户端leaf的服务器组件。
但无论我做什么,当我在Vercel上部署时,我都无法让<title>或任何<meta>标记出现在服务器源代码的<head>中。
下面是我所做的工作的摘要。(注:在next.config.js中需要experimental: { appDir: true })。

// app/head.tsx

export default function Head() {
  return (
    <>
      <title>My App</title>
    </>
  )
}
// app/layout.tsx

import { SSRThemeProvider } from "lib/SSR/theme";
import React from "react";
import theme from "styles/appTheme";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <React.StrictMode>
      <html lang="en" key="root">
        <head />
      </html>
      {/* ^ duplicate html/head outside of client-only provider */}
      <SSRThemeProvider theme={theme} key="theme-provider">
        <html lang="en" key="root">
          {/*
            <head /> will contain the components returned by the nearest parent
            head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
          */}
          <head />
          <body>{children}</body>
        </html>
      </SSRThemeProvider>
    </React.StrictMode>
  );
}
// app/home.tsx

import { HomePage } from "content/home";
import { getPlaceholderImages } from "lib/SSR/placeholderImages";

export default async function HybridHome() {
  const images = await getPlaceholderImages();
  //                      ^ server-side stuff with sharp
  return <HomePage placeholderImages={images} />;
}
"use client"; 
// ^ client only directive
// lib/SSR/theme.provider.tsx

import { ThemeProvider } from "@mui/material";
import { DefaultTheme, ThemeProviderProps } from "@mui/styles";
import React from "react";

export const SSRThemeProvider = <T = DefaultTheme,>(
  props: ThemeProviderProps<T>
): React.ReactElement<ThemeProviderProps<T>> => {
  return (
    <React.StrictMode>
      <ThemeProvider {...props} />
    </React.StrictMode>
  );
};
"use client";
// ^ client only here too
// content/home.tsx

import { CssBaseline } from "@mui/material";
import RootStyleRegistry from "lib/SSR/emotion";
import { usePathname, useSearchParams } from "next/navigation";

export const HomePage = () => {

  const pathname = usePathname();
  const searchParams = useSearchParams();
  
  return (
    <>
      <CssBaseline key="css-baseline" />
      <RootStyleRegistry>
        <main>
          {/* Words go here /*}
        </main>
      </RootStyleRegistry>
    </>
  );
};
zsohkypk

zsohkypk1#

head.tsx组件应该返回一个片段,而不是head
试试这个

return (
    <>
      <title>My App</title>
    </>
  )
}

其次,你不需要在任何地方导入这个组件,它会自动添加到相应页面和子页面的head部分。

31moq8wy

31moq8wy2#

啊,原来这是useQueryParams中每个https://github.com/vercel/next.js/issues/44868的一个实际错误。
我暂时用一个定制的钩子替换了它,现在我所有的标记都显示出来了,即使我使用@MayankKumar的建议,直接使用<head />而不使用其他组件。
这是我的钩子,而门票仍然开放:

import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import isEqual from "react-fast-compare";

interface UrlEvents {
  routeChanged?: ({
    pathname,
    location,
  }: {
    pathname: string | null;
    location: Location | null;
  }) => void;
}

export const useNavigation = ({ on }: { on?: UrlEvents }) => {
  const pathname = usePathname();
  const [location, setLocation] = useState<Location>();

  const { routeChanged } = on || {};

  useEffect(() => {
    if (pathname && typeof window !== "undefined") {
      window.addEventListener("popstate", () => {
        if (!isEqual(window.location, location)) {
          setLocation(window.location);
          routeChanged?.({
            pathname,
            location: window.location,
          });
        }
      });

      return () => {
        window.removeEventListener("popstate", () => {});
      };
    }
  }, [location, pathname, routeChanged]);

  return location;
};

相关问题