如何优化你的Gatsby博客的搜索引擎?

201 阅读4分钟

老兄,这个工具真的很厉害 🔥

我使用Gatsby已经有一个月了,在花时间学习之后,我真的很喜欢它。到目前为止,我已经用Gatsby把这个博客、一个宠物项目的登陆页面、一个客户的投资组合网站和我的企业的博客放在一起,我有一种强烈的感觉,我将在更多的项目中使用它。作为一个全栈的JavaScript-er,任何不需要使用WordPress的日子都是好日子😃(开玩笑,WordPress其实是很好用的)。

建立博客最重要的部分之一是确保你的每篇博文都有适当的元标签,以便当你的内容被搜索引擎分享和索引时,显示正确的标题、描述和关键词。Gatsby文档指出使用gatsby-plugin-react-helmet插件来为你的博客添加元标签,但没有给出一个很好的具体例子,说明你如何设置一个漂亮的可重复使用的组件来做这个。这就是我今天要与你分享的内容。

我们将创建一个简单的可重复使用的组件,你可以在你的博客文章模板中使用。

把它全部连接起来

1.创建一个配置文件

首先,我们要创建一个这样的配置文件。

app/config/index.js

const Config = {
  title: 'Khalil Stemmler - Software Developer / Designer',
  twitter: 'https://twitter.com/stemmlerjs',
  url: 'https://khalilstemmler.com',
  logo: 'https://khalilstemmler.com/img/me.jpg'
  
}

export default Config

对于每篇博文,我们要让博文的标题显示为标题。但是,对于每一个不是博客文章的其他路线,我们可以配置title ,作为一个后备。

把你的logotwitter 和网站url 也挂在这里。

2.创建可重复使用的组件

这个文件接收了isBlogPost,postData, 和postImage 道具,并建立了一个以JSON-LD格式描述数据的组件。它还将你的博客文章数据注入到元标签中。

app/components/SEO.js

import path from 'path';
import React from 'react';
import Helmet from 'react-helmet';
import PropTypes from 'prop-types';
import * as config from '../config';

const getSchemaOrgJSONLD = ({
  isBlogPost,
  url,
  title,
  image,
  description,
  datePublished,
}) => {
  const schemaOrgJSONLD = [
    {
      '@context': 'http://schema.org',
      '@type': 'WebSite',
      url,
      name: title,
      alternateName: config.title,
    },
  ];

  return isBlogPost
    ? [
        ...schemaOrgJSONLD,
        {
          '@context': 'https://khalilstemmler.com',
          '@type': 'BreadcrumbList',
          itemListElement: [
            {
              '@type': 'ListItem',
              position: 1,
              item: {
                '@id': url,
                name: title,
                image,
              },
            },
          ],
        },
        {
          '@context': 'https://khalilstemmler.com',
          '@type': 'BlogPosting',
          url,
          name: title,
          alternateName: config.title,
          headline: title,
          image: {
            '@type': 'ImageObject',
            url: image,
          },
          description,
          author: {
            '@type': 'Person',
            name: 'Khalil Stemmler',
          },
          publisher: {
            '@type': 'Organization',
            url: 'https://khalilstemmler.com',
            logo: config.logo,
            name: 'Khalil Stemmler',
          },
          mainEntityOfPage: {
            '@type': 'WebSite',
            '@id': config.url,
          },
          datePublished,
        },
      ]
    : schemaOrgJSONLD;
};

const SEO = ({ postData, postImage, isBlogPost }) => {
  const postMeta = postData || {};

  const title = postMeta.title || config.title;
  const description =
    postMeta.description || postData.excerpt || config.description;
  const image = `${config.url}${postImage}` || config.image;
  const slug = postMeta.slug;
  const url = postMeta.slug
    ? `${config.url}${postMeta.slug}`
    : config.url;
  const datePublished = isBlogPost ? postMeta.date : false;

  const schemaOrgJSONLD = getSchemaOrgJSONLD({
    isBlogPost,
    url,
    title,
    image,
    description,
    datePublished,
  });
  
  return (
    <Helmet>
      {/* General tags */}
      <meta name="description" content={description} />
      <meta name="image" content={image} />

      {/* Schema.org tags */}
      <script type="application/ld+json">
        {JSON.stringify(schemaOrgJSONLD)}
      </script>

      {/* OpenGraph tags */}
      <meta property="og:url" content={url} />
      {isBlogPost ? <meta property="og:type" content="article" /> : null}
      <meta property="og:title" content={title} />
      <meta property="og:description" content={description} />
      <meta property="og:image" content={image} />

      {/* Twitter Card tags */}
      <meta name="twitter:card" content="summary_large_image" />
      <meta name="twitter:creator" content={config.twitter} />
      <meta name="twitter:title" content={title} />
      <meta name="twitter:description" content={description} />
      <meta name="twitter:image" content={image} />
    </Helmet>
  );
};

SEO.propTypes = {
  isBlogPost: PropTypes.bool,
  postData: PropTypes.shape({
    frontmatter: PropTypes.any,
    excerpt: PropTypes.any,
  }).isRequired,
  postImage: PropTypes.string,
};

SEO.defaultProps = {
  isBlogPost: false,
  postImage: null,
};

export default SEO;

Helmet最酷的地方在于,你可以在任何嵌套的组件结构中多次使用它,但结果总是来自它的最后一个实例。因此,如果我们在我们的layout/index.js ,当我们把它挂在我们的app/templates/blog-post.js ,它将从我们的博客文章模板中获取Helmet的结果。这是因为Gatsby在渲染我们的模板文件之前先渲染了我们的布局。

3.把它挂在你的博客文章模板文件中

最后要做的是在你的博文模板文件中实际勾连它。下面是我的模板文件的样子。

app/templates/blog-post.js

import React from 'react'
import PropTypes from 'prop-types'
import { kebabCase } from 'lodash'
import Helmet from 'react-helmet'
import Link from 'gatsby-link'
import Content, { HTMLContent } from '../components/Content'
import ReactDisqusComments from 'react-disqus-comments';
import SEO from '../components/SEO';

import helpers from '../helpers'

import styles from '../styles/Blog.module.css'

function getUniquePageIdentifier () {
  return typeof window !== 'undefined' && window.location.href
      ? typeof window !== 'undefined' && window.location.href
      : 'https://khalilstemmler.com'
}

export const BlogPostTemplate = ({
  content,
  contentComponent,
  description,
  tags,
  title,
  helmet,
  date,
  image,
  category
}) => {
  const PostContent = contentComponent || Content

  return (
    <section>
      {helmet || ''}
      <div>
        <div>
          <div style= {{ margin: '0 auto'}} className="column is-10">
            <h1 className="title is-size-2 has-text-weight-bold is-bold-light">
              {title}
            </h1>
            
            <h4 className={styles.date}>in <Link className={styles.category} to={`/blog/categories/${kebabCase(category)}/`}>{category}</Link></h4>

            <div>
              <img src={image}/>
            </div>

            <p>{description}</p>
            <PostContent content={content} />
            {tags && tags.length ? (
              <div style={{ marginTop: `4rem` }}>
                <h4>Tags</h4>
                <ul className="taglist">
                  {tags.map(tag => (
                    <li key={tag + `tag`}>
                      <Link to={`/blog/tags/${kebabCase(tag)}/`}>{tag}</Link>
                    </li>
                  ))}
                </ul>
              </div>
            ) : null}
            <ReactDisqusComments
              shortname="khalilstemmler-com"
              identifier={ getUniquePageIdentifier() }
              title={title}
              url={ getUniquePageIdentifier() }
              />
          </div>
        </div>
      </div>
    </section>
  )
}

BlogPostTemplate.propTypes = {
  content: PropTypes.string.isRequired,
  contentComponent: PropTypes.func,
  description: PropTypes.string,
  title: PropTypes.string,
  helmet: PropTypes.instanceOf(Helmet),
}

const BlogPost = ({ data }) => {
  let { markdownRemark: post } = data;

  post = Object.assign({}, post, post.fields, post.frontmatter)

  return (
    <BlogPostTemplate
      content={post.html}
      contentComponent={HTMLContent}
      description={post.description}
      helmet={
        <SEO 
          isBlogPost={true}
          postData={post}
          postImage={post.image}
        />}
      tags={post.tags}
      title={post.title}
      date={post.date}
      image={post.image}
      category={post.category}
    />
  )
}

BlogPost.propTypes = {
  data: PropTypes.shape({
    markdownRemark: PropTypes.object,
  }),
}

export default BlogPost

export const pageQuery = graphql`
  query BlogPostByID($id: String!) {
    markdownRemark(id: { eq: $id }) {
      id
      html
      fields {
        slug
      }
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        title
        description
        tags
        image
        category
      }
    }
  }
`

就这样了

很简单吧?这就是你如何在Gatsby中用Helmet创建一个可重复使用的SEO组件。现在你应该能够使用像Facebook的URL调试器这样的东西,并测试它是否正常工作。如果你有其他类型的集合和模板,如标签、类别、产品、服务等,你可以扩展这一点,使这些页面也得到SEO优化。我将在不久的将来这样做。