老兄,这个工具真的很厉害 🔥
我使用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 ,作为一个后备。
把你的logo 、twitter 和网站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优化。我将在不久的将来这样做。