【发布时间】:2021-06-17 09:15:02
【问题描述】:
感觉这个问题已经被问了一百遍了。所以,这可能是一个很难的问题。我将为我的案例提供更多细节。也许会有所帮助。
我正在使用 Next.js ^10.0.9 和 next-seo ^4.22.0。我可以在 devtools 中看到元标记,但 FB 和 Twitter 以及许多其他元标记验证器无法获取它们。
从这里和其他地方的其他几个问题来看,似乎有一些共识,只要它不在源中,即,如果我们“检查源”并且看不到它,那么爬虫就无法使用它。据我了解,这是因为这些机器人不运行呈现元标记所需的 JavaScript。
来源
此页面源应包含描述、标题、图像和其他一些内容的 opengraph 元标记,但它没有:
<!DOCTYPE html>
<html lang="en">
<head>
<style data-next-hide-fouc="true">
body{display:none}
</style>
<noscript data-next-hide-fouc="true">
<style>
body{display:block}
</style>
</noscript>
<meta charSet="utf-8"/>
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width"/>
<meta name="next-head-count" content="2"/>
<noscript data-n-css=""></noscript>
<link rel="preload" href="/_next/static/chunks/main.js?ts=1616232654196" as="script"/>
<link rel="preload" href="/_next/static/chunks/webpack.js?ts=1616232654196" as="script"/>
<link rel="preload" href="/_next/static/chunks/pages/_app.js?ts=1616232654196" as="script"/>
<link rel="preload" href="/_next/static/chunks/pages/index.js?ts=1616232654196" as="script"/>
<noscript id="__next_css__DO_NOT_USE__"></noscript>
<style id="jss-server-side">html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
*, *::before, *::after {
box-sizing: inherit;
}
strong, b {
font-weight: 700;
}
body {
color: rgba(0, 0, 0, 0.87);
margin: 0;
font-size: 0.875rem;
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
font-weight: 400;
line-height: 1.43;
letter-spacing: 0.01071em;
background-color: #c3d0f5;
}
@media print {
body {
background-color: #fff;
}
}
body::backdrop {
background-color: #c3d0f5;
}</style>
</head>
<body>
<div id="__next">
<div class="Loading__Center-sc-9gpo7v-0 dctQei">
<div style="width:100%;height:100%;overflow:hidden;margin:0 auto;outline:none" title="" role="button" aria-label="animation" tabindex="0">
</div>
<h2>This is you. This is how you wait.</h2>
</div>
</div>
<script src="/_next/static/chunks/react-refresh.js?ts=1616232654196"></script>
<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/","query":{},"buildId":"development","runtimeConfig":{"APP_NAME":"example","APP_DESCRIPTION":"Discover examples of examples of examples.","APP_URL":"https://example.com","nextExport":true,"autoExport":true,"isFallback":false}</script>
<script nomodule="" src="/_next/static/chunks/polyfills.js?ts=1616232654196"></script>
<script src="/_next/static/chunks/main.js?ts=1616232654196"></script>
<script src="/_next/static/chunks/webpack.js?ts=1616232654196"></script>
<script src="/_next/static/chunks/pages/_app.js?ts=1616232654196"></script>
<script src="/_next/static/chunks/pages/index.js?ts=1616232654196"></script>
<script src="/_next/static/development/_buildManifest.js?ts=1616232654196"></script>
<script src="/_next/static/development/_ssgManifest.js?ts=1616232654196"></script>
</body>
</html>
构建时生成的页面?
让我感到困惑的是 Next.js 没有渲染它们。根据docs:
默认情况下,Next.js 会预渲染每个页面。这意味着 Next.js 预先为每个页面生成 HTML,而不是全部完成 通过客户端 JavaScript。预渲染可以带来更好的效果 性能和 SEO。
除非我看错了,这意味着只要我使用默认设置,页面将通过 SSR 或静态生成呈现。对吧?
另外,上面的源码中提到了"nextExport":true,"autoExport":true,我认为这表明页面应该在构建时创建。
我的代码可能发生了一些变化,但我很确定它们没有偏离 SSR 或静态生成。
代码的高级视图
在某个时候,我添加了 _document.tsx 和 _app.tsx。
我没有对 _document.tsx 进行太多更改。经过一些实验后,我认为从next/document 获得Head 至关重要:
import Document, { Head, Html, Main, NextScript } from "next/document";
import React from "react";
import { ServerStyleSheets } from "@material-ui/core/styles";
import theme from "../styles/theme";
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
_app.tsx 有更多变化,但我认为不会影响元标记。如果我在这里手动将元标记手动放置在Head 中,它们会显示在源代码中。默认情况下效果很好,但我希望能够以编程方式为每个页面执行此操作。
import { useEffect, useState } from "react";
import { AppProps } from "next/app";
import CssBaseline from "@material-ui/core/CssBaseline";
import Layout from "../components/Layout";
import { ThemeProvider } from "@material-ui/core/styles";
import { User } from "../context/user";
import theme from "../styles/theme";
export default function App({ Component, pageProps }: AppProps) {
const [user, setUser] = useState({
auth: null,
loading: true,
});
useEffect(() => {
const getUser = async () => {
const res = await fetch("/api/auth/me");
const auth = res.ok ? await res.json() : null;
setUser({ auth, loading: false });
};
getUser();
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles) {
jssStyles &&
jssStyles.parentElement &&
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<>
<User.Provider value={{ user, setUser }}>
<ThemeProvider theme={theme}>
<CssBaseline />
<Layout>
<Component {...pageProps} />
</Layout>
</ThemeProvider>
</User.Provider>
</>
);
}
我使用 next-seo 创建了一个Head 组件。它做了很多工作。也许只是注意它直接返回NextSeo,而不是把它放入React Fragment或其他组件中:
import { NextPage } from "next";
import { NextSeo } from "next-seo";
import React from "react";
import getConfig from "next/config";
const { publicRuntimeConfig } = getConfig();
interface Props {
page?: string;
description?: string;
image?: {
url: string;
alt: string;
};
}
const appDescription = publicRuntimeConfig.APP_DESCRIPTION;
const appName = publicRuntimeConfig.APP_NAME;
const appUrl = publicRuntimeConfig.APP_URL;
const twitter = publicRuntimeConfig.TWITTER;
const PageHead: NextPage<Props> = ({ page, description, image }: Props) => {
const pageTitle = page ? `${page} | ${appName}` : appName;
const pageDescription = description ?? appDescription;
let pageUrl;
let isItemPage;
if (typeof window !== "undefined") {
pageUrl = window.location.href ?? appUrl;
isItemPage = window.location.pathname.includes("item");
}
const disallowRobot = isItemPage ? true : false;
const pageImage = image ?? {
url: `${appUrl}/logo.png`,
width: 400,
height: 400,
alt: `${appName} logo`,
};
return (
<NextSeo
title={pageTitle}
description={pageDescription}
canonical={pageUrl}
openGraph={{
url: pageUrl,
title: pageTitle,
description: pageDescription,
images: [pageImage],
site_name: appName,
}}
twitter={{
handle: twitter,
site: twitter,
cardType: "summary_large_image",
}}
noindex={disallowRobot}
nofollow={disallowRobot}
/>
);
};
export default PageHead;
这是它在组件/页面中的使用方式。此页面包含来自第三方 API 的动态内容,并且 URL 取决于该内容。我不确定,但我认为这个页面是通过静态生成创建的:
import Head from "../../components/Head";
interface Props {
item: ContentProps;
}
const MostLikedThings: NextPage<Props> = ({ item }: Props) => {
...
return (
<>
<Head
page={item.title}
description={item.synopsis}
image={...}
/>
<MainWrapper>
...
</MainWrapper>
</>
);
};
export default MostLikedThings;
export async function getStaticPaths() {
return {
paths: [],
fallback: true,
};
}
export async function getStaticProps({ params }: { params: { id: string } }) {
const item = await (
await fetch(`/api/content`)
).json();
return {
props: { item },
revalidate: 86400,
};
}
不需要外部内容的更简单的页面如下面的代码所示。我相信它也是使用静态生成制作的,因为(据我所知)我没有使用 getServerSideProps :
import Head from "../components/Head";
import Main from "../apps/main/index";
import { NextPage } from "next";
const Home: NextPage = () => {
return (
<>
<Head page="Home" />
<Main />
</>
);
};
export default Home;
我相信这两个页面都是通过静态生成生成的,对吧?在这种情况下,它们都是在构建时生成的。这意味着元标记应该在源代码中。那么,为什么不呢?
做了几次尝试
我尝试了很多方法,包括:
- 使用
getServerSideProps试图以某种方式更明确地确保页面是在服务器端生成的 - 从 _app.tsx 和 _document.tsx 中删除
Head - 仅使用页面中的html标签
head手动设置元标签 - 直接在页面中使用
next/head而不是next-seo手动设置元标签:
<Head>
...
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content={twitter} />
<meta name="twitter:creator" content={twitter} />
...
</Head>
它们都不起作用。
最后要注意的是,我读到here:
需要包含标题、元数据或任何其他元素(例如脚本) 作为 Head 元素的直接子元素,或包裹到最多一个
或数组的级别——否则标签将不会 正确选择客户端导航。
我想我已经确定了这一点。
我读过的许多问题中的几个:
- Meta Tags does not appear in View Source Code - Facebook Crawler issue - SEO
- Next JS - Meta Tags Missing in Source Code
- META tags in Next.js React App are not readable by BrowseSEO
- Page Content is not showing in source code
我错过了什么吗?
谢谢。
【问题讨论】:
-
能否包含页面源代码(至少通过元标记,仅作为检查)?有时,服务器提供的页面源与 devtools 中显示的代码不同。
-
@RichDeBourke 已添加,问题顶部的第一块代码。谢谢你,我完全忘了添加它。
-
您网页的 HTML 代码不包含您的 OG 元数据。由于您可以使用 devtools 查看 OG 数据,因此这些标签可能是由客户端 JavaScript 生成的。 Facebook 不会运行任何 JavaScript,因此他们将无法看到您的标签。我没有在我的任何网站上使用 React 或 next.js,所以我不确定它是如何工作的,但似乎你想做static generation。
标签: javascript reactjs seo next.js server-side-rendering