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
。
我希望这个回答能帮助遇到类似问题的人。
我遇到 this landing site 的 CLS 问题。如这张照片 /
.
我一定是漏掉了一些关于 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
。
我希望这个回答能帮助遇到类似问题的人。