React SPA 中某些页面的动态元描述

Dynamic meta descriptions for certain pages in React SPA

我用 React 构建了一个博客,它从 api 中动态获取每个博客条目的数据。 该页面的内容显然根据路线看起来有所不同,例如mysite.com/blog/1mysite.com/blog/2、...

我想要实现的是根据 url 从 api 获取的数据动态更改元描述。特别是 og:titleog:descriptionog:image。这样的事情甚至可能吗?

我阅读了有关 SSR/Next.JS 或 Gatsby 的信息,但我不确定如果从 api 调用接收到数据,这是否有效。

我知道 SSR 会在服务器上呈现内容,因此它允许 Google 抓取页面,但不会排除 api 调用吗? 我也明白 Gatsby 构建静态站点,但对我来说这行不通,因为 api 调用是动态的,不能构建到静态站点中。

我非常感谢能给我指明正确方向的提示。

I understand that SSR would render the content on the server, hence it allows Google to crawl the pages but wouldn't that exclude api calls? I also understand that Gatsby builds static sites but for me that wouldn't work because the api calls are dynamic and cannot be built into a static site.

我认为您误解了 Gatsby 的工作原理。

总而言之,Gatsby 从动态数据(API 调用)生成静态页面,因此,当您 运行 gatsby developgatsby build 命令时,Gatsby 会获取数据来自来源(在本例中来自您的 API)并生成动态页面(mysite.com/blog/1mysite.com/blog/2 等)。

此数据是静态的,这意味着这些来源的每次更改都会迫使您重新获取数据以显示更改(在生产中意味着新的部署)但是,一旦您的页面是静态构建的,您可以即时构建您的 SEO 数据。

这是他们以前在旧网站上使用的旧 Gatsby 工作流程,但我发现它非常不言自明:

在构建时,您的站点是静态生成的,因此您可以使用自定义 SEO 组件或您想要的任何内容完全自定义您的 SEO 要求。大多数启动器都带有一个可以使用的 SEO 组件:

function Seo({ description, lang, meta, title }) {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `
  )

  const metaDescription = description || site.siteMetadata.description
  const defaultTitle = site.siteMetadata?.title

  return (
    <Helmet
      htmlAttributes={{
        lang,
      }}
      title={title}
      titleTemplate={defaultTitle ? `%s | ${defaultTitle}` : null}
      meta={[
        {
          name: `description`,
          content: metaDescription,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: metaDescription,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: site.siteMetadata?.author || ``,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: metaDescription,
        },
      ].concat(meta)}
    />
  )
}

由于该组件将 descriptionlangmetatitle 分别作为 props,您可以将其添加到您的博客模板中获取将静态构建的动态数据,这意味着,当部署您的站点时,该数据已经构建,因此 Google 爬虫将立即获取它。

const YourBlogPostTemplate = ({ data }) =>{    
   
   return <article>
   <SEO title={data.allAPIData.frontmatter.title} />
   <h1>I'm the title: {data.allAPIData.frontmatter.title}</h1>
   </article>
}

注意:我不会完全自定义它以避免扩展答案,但请理解。

SEO 组件将在构建时使用 title 和其余字段。

您的 API 调用是在页面构建之前获取的,因此,在构建页面时,您的数据已经是静态的。

有一篇非常好的文章 here 描述了如何以最简单的方式解决此问题。

基本上,我需要在我当前的 React 应用程序之上放置一个简单的 NodeJS 服务器。服务器现在提供构建目录中的文件。对于某些路由,我只是将 __PAGE_META__ 占位符替换为 Google、Facebook、Twitter 等

所需的元标记

这里用一张简单的图片来描述流程:

以及 NodeJS 部分的简单代码片段:

const path = require("path")
const express = require("express")
const app = express()
const fs = require("fs")
//
const pathToIndex = path.join(__dirname, "build/index.html")
app.get("/", (req, res) => {
  const raw = fs.readFileSync(pathToIndex)
  const pageTitle = "Homepage - Welcome to my page"
  const updated = raw.replace("__PAGE_META__", `<title>${pageTitle}</title>`)
  res.send(updated)
})
//
app.use(express.static(path.join(__dirname, "build")))
app.get("*", (req, res) =>
  res.sendFile(path.join(__dirname, "build/index.html"))
)
const port = process.env.PORT || 5000
app.listen(port, () => {
  console.log(`Server started on port ${port}`)
})

对于动态站点,可以调用一个api,页面meta可以适当替换为信息。