Next.js SSG 站点的累积布局偏移问题

Next.js cumulative layout shift issue with SSG site

我遇到 this landing site 的 CLS 问题。如这张照片 /.

所示,着陆点是在 Next.js(SSG) 中静态生成的

我一定是漏掉了一些关于 Next.js SSG 的东西。当我打开生成的 index.html 文件时,并非所有 HTML 内容都存在。当页面加载时,看起来 Next.js 在页面加载时注入了这些缺失的部分...难道整个 HTML 不应该静态生成并在 index.html 站点中可用吗?我在这里遗漏或不理解什么?

这是页面的数据获取:

import Head from "next/head"
import { 
  Box,
  Center,
  Container,
  Heading,
  Tag,
} from "@chakra-ui/react";
import { request } from "/lib/datocms";
import {
  MAIN_MENU_QUERY,
  BLOG_POSTS_QUERY,
  BLOG_CATEGORIES_QUERY,
  HOME_PAGE_QUERY,
} from "/lib/queries";
import BlockRender from "/components/blocks/BlockRender";
import ChakraNextLink from "/components/atoms/ChakraNextLink";
import BlogGrid from "/components/blog/BlogGrid"
import { renderMetaTags } from "react-datocms";

...

export const getStaticProps = async ({ preview }) => {
  const graphqlRequest = {
    query: MAIN_MENU_QUERY,
    preview
  }
  const data = await request(graphqlRequest);

  const homePageGraphqlRequest = {
    query: HOME_PAGE_QUERY,
    preview
  }
  const homePage = await request(homePageGraphqlRequest);
  const pageData = homePage?.homePage;

  const blogPostsGraphqlRequest = {
    query: BLOG_POSTS_QUERY,
    preview,
    variables: {
      count: homePage.homePage.blogPostCount ?? 0
    }
  }
  const blogPosts = await request(blogPostsGraphqlRequest);

  const blogCategoriesGraphqlRequest = {
    query: BLOG_CATEGORIES_QUERY,
    preview
  }
  const blogCategories = await request(blogCategoriesGraphqlRequest);

  return {
    props: {
      data,
      pageData,
      blogPosts,
      blogCategories
    },
  };
};

这是我的索引页面的代码:

export default function Home({ data, pageData, blogPosts, blogCategories }) {
  const metaTags = pageData.seo.concat(data.site.favicon);

  return (
    <>
      <Head>
        {renderMetaTags(metaTags)}
      </Head>

      # all this content is not in the generated Index.html file and causes CLS issue
      {pageData.content && 
        pageData.content.map((block, key) => <BlockRender key={key} block={block} />)
      }

      # this is in the generated index.html file
      <Box w="full" py="12">
        {pageData.showBlogSection && (
          <BlogSection posts={blogPosts} categories={blogCategories}>
        )}
      </Box>
    </>
  )
}

问题出在从我的后端无头 CMS 动态加载数据组件的组件中。我改变了这个:

import dynamic from "next/dynamic";

export default function BlockRender({block}) {
  switch(block.__typename) {
    case "ContactSectionRecord":
      const ContactSection = dynamic(() => import("../page/ContactSection"));
      return <ContactSection block={block} />;
    case "StructuredTextSectionRecord":
      const StructuredTextSection = dynamic(() => import("../page/StructuredTextSection"));
      return <StructuredTextSection block={block} />;
    case "VirtualShowroomSectionRecord":
      const VirtualShowroomSection = dynamic(() => import("../page/VirtualShowroomSection"));
      return <VirtualShowroomSection block={block} />;
    case "HeroSectionOneRecord":
      const HeroOneSection = dynamic(() => import("../page/HeroOneSection"));
      return <HeroOneSection block={block} />;  
    case "HeroSectionTwoRecord":
      const HeroTwoSection = dynamic(() => import("../page/HeroTwoSection"));
      return <HeroTwoSection block={block} />;    
    case "PanogaSectionRecord":
      const PanogaSection = dynamic(() => import("../page/PanogaSection"));
      return <PanogaSection block={block} />;    
    case "VirtualShowroomLandingSectionRecord":
      const VirtualShowroomLandingSection = dynamic(() => import("../page/VirtualShowroomLandingSection"));
      return <VirtualShowroomLandingSection block={block} />;    
    case "AktualnaPonudbaSectionRecord":
      const AktualnaPonudbaSection = dynamic(() => import("../page/AktualnaPonudbaSection"));
      return <AktualnaPonudbaSection block={block} />;    
    case "ParalaxSectionRecord":
      const ParalaxSection = dynamic(() => import("../page/ParalaxSection"));
      return <ParalaxSection block={block} />;    
    case "ColumnBlockRecord":
      const ColumnBlockSection = dynamic(() => import("../page/ColumnBlockSection"));
      return <ColumnBlockSection block={block} />;    
    default:
      return <></>
  }
}

进入这个:

import dynamic from "next/dynamic";

const ContactSection = dynamic(() => import("../page/ContactSection"));
const StructuredTextSection = dynamic(() => import("../page/StructuredTextSection"));
const VirtualShowroomSection = dynamic(() => import("../page/VirtualShowroomSection"));
const HeroOneSection = dynamic(() => import("../page/HeroOneSection"));
const HeroTwoSection = dynamic(() => import("../page/HeroTwoSection"));
const PanogaSection = dynamic(() => import("../page/PanogaSection"));
const VirtualShowroomLandingSection = dynamic(() => import("../page/VirtualShowroomLandingSection"));
const AktualnaPonudbaSection = dynamic(() => import("../page/AktualnaPonudbaSection"));
const ParalaxSection = dynamic(() => import("../page/ParalaxSection"));
const ColumnBlockSection = dynamic(() => import("../page/ColumnBlockSection"));

export default function BlockRender({block}) {
  switch(block.__typename) {
    case "ContactSectionRecord":
      return <ContactSection block={block} />;
    case "StructuredTextSectionRecord":
      return <StructuredTextSection block={block} />;
    case "VirtualShowroomSectionRecord":
      return <VirtualShowroomSection block={block} />;
    case "HeroSectionOneRecord":
      return <HeroOneSection block={block} />;  
    case "HeroSectionTwoRecord":
      return <HeroTwoSection block={block} />;    
    case "PanogaSectionRecord":
      return <PanogaSection block={block} />;    
    case "VirtualShowroomLandingSectionRecord":
      return <VirtualShowroomLandingSection block={block} />;    
    case "AktualnaPonudbaSectionRecord":
      return <AktualnaPonudbaSection block={block} />;    
    case "ParalaxSectionRecord":
      return <ParalaxSection block={block} />;    
    case "ColumnBlockRecord":
      return <ColumnBlockSection block={block} />;    
    default:
      return <></>
  }
}

CLS 从 0.85 下降到 0.1

我希望这个回答能帮助遇到类似问题的人。