用 Gatsby 搭建静态博客 2

1,155 阅读3分钟

[原文地址]

上一篇做了导航菜单、弄出了博文列表、博文预览及翻页器。

基本的功能弄的差不多了,不过还需要用这些基本功能再完善一下博客。

首页也需要改改、返回顶部的按钮、还有本人比较喜欢的滚动条进度条、统计、Google被搜索等等。


6. 改版首页

博客首页的传统实现方式是显示部分博文,下方再加个 Read More 的按钮。

只有一种首页是不是太单调了,在特殊节日我想搞个特效页面什么的展示在首页。 所以,我弄了个配置,如果是普通日子,就默认用传统的有分页,博文部分显示的首页。 如果是特殊节日,根据开发的特效页面不同,也能很快的进行切换。

首先,我们把原先的入口文件 src/pages/index.js 更名或删除。

并创建模板文件 src/templates/home.js

import React from 'react'

import Layout from '../components/layout'
import HomePage from '../components/Home'
import SEO from '../components/seo'

const Template = ({ pageContext }) => (
	<Layout>
		<SEO title="Boliball" />
		<HomePage type="normal" pageContext={pageContext} />
	</Layout>
)

export default Template

通过这种方式,我们可以把给入模板文件的页面上下文转移到 src/components/Home/index.jsx 里。

我们再改下高级渲染方式 gatsby-node.js,把组织数据部分弄一弄。

...
// 生成首页
const HomeTemplate = path.resolve('./src/templates/home.js')
createPaginatedPages({
	edges        : result.data.allMarkdownRemark.edges,
	createPage   : createPage,
	pageTemplate : HomeTemplate,
	pageLength   : 5,
	pathPrefix   : ''
})
...

上下文数据有了,模板也有了,弄一弄首页 Component 吧。

先弄一个组件路由。

import React from 'react'
import PropTypes from 'prop-types'

import NormalHomePage from './normal'
import HolidayHomePage from './holiday'

const HomeComponentMapping = {
	normal  : NormalHomePage,
	holiday : HolidayHomePage
}

const HomePage = ({ type, pageContext }) => {
	const TheHomePage = HomeComponentMapping[type]

	return <TheHomePage pageContext={pageContext} />
}

HomePage.propTypes = {
	type: PropTypes.string
}

HomePage.defaultProps = {
	type: 'normal'
}

export default HomePage

再把带分页的首页弄一弄。

import React from 'react'
import { Link } from 'gatsby'

import Paginator from '../Paginator'

import './style.css'

const { Fragment } = React

const NormalHomePage = ({ pageContext }) => {
	const {
		group,
		index,
		pageCount
	} = pageContext

	return <Fragment>
		{group.map(({ node }) => (
			<div className="normal-homepage-item" key={node.id}>
				<h3><Link className="title" to={node.frontmatter.path}>{node.frontmatter.title}</Link></h3>
				<small>Posted by {node.frontmatter.author} on {node.frontmatter.date}</small>
				<br/>
				<br/>
				<div dangerouslySetInnerHTML={{__html: node.html.split('<!--more-->')[0]}}></div>
				<Link to={node.frontmatter.path}>Read More</Link>
				<br/>
			</div>
		))}
		<Paginator index={index} pageCount={pageCount} relativeUrl="" />
	</Fragment>
}

export default NormalHomePage

注:博文里需要有识别符 <!--more-->。之所以首页能只显示一部分,是因为使用这个识别符做了拆分。


7. 返回顶部

返回顶部的实现方式有很多种,这里只作为示例。

import React from 'react'

import './style.css'

class BackToTop extends React.Component {
	constructor(props) {
		super(props)

		this.state = {
			show: false
		}

		this.timer = undefined
		this.scrollHandle = this.scrollHandle.bind(this)
	}

	componentDidMount() {
		window.addEventListener('scroll', this.scrollHandle)
	}

	componentWillUnmount() {
		window.removeEventListener('scroll', this.scrollHandle)
	}

	scrollHandle() {
		clearTimeout(this.timer)
		this.timer = setTimeout(() => {
			this.setState({
				show: document.body.scrollTop > 700 || document.documentElement.scrollTop > 700
			})
		}, 100)
	}

	render() {
		return this.state.show && <div className="back-to-top">
			<button onClick={() => { window.scrollTo(0, 0) }}>⬆︎</button>
		</div>
	}
}

export default BackToTop

8. 滚动条进度条

import React from 'react'

import './style.css'

class ScrollIndicator extends React.Component {
	constructor(props) {
		super(props)

		this.scrollHandle = this.scrollHandle.bind(this)
	}

	componentDidMount() {
		window.addEventListener('scroll', this.scrollHandle)
	}

	componentWillUnmount() {
		window.removeEventListener('scroll', this.scrollHandle)
	}

	scrollHandle() {
		let winScroll = document.body.scrollTop || document.documentElement.scrollTop
		let height = document.documentElement.scrollHeight - document.documentElement.clientHeight
		let scrolled = (winScroll / height) * 100
		this.refs.progress_bar.style.width = `${scrolled}%`
	}

	render() {
		return <div className="scroll-indicator">
			<div ref="progress_bar" className="progress-bar"></div>
		</div>
	}
}

export default ScrollIndicator

9. 百度统计 & 被 Google 搜索

Google统计的脚本在国内有时候加载不成功,都懂得。

不废话,安装百度统计的中间件。

npm i gatsby-plugin-baidu-tongji

修改下配置文件 gatsby-config.js

...
plugins: [
	...
	{
		resolve: 'gatsby-plugin-baidu-tongji',
		options: {
			siteid: 'FiJx5NYvSMLLCLCO9Ep9AmCEYpnhLZEo',
			head: false
		}
	}
	...
]
...

被 Google 搜索需要在 search.google.com 上注册自己的站点。

站点的验证方式很多,这里用举例 meta 方式。修改 src/components/seo.js

...
meta={[
	...
	{
		name: `google-site-verification`,
		content: 'axWre5Mc1WeCJHuCSLAALonVYdzjco_Ao__dvd3CsYX'
	}
	...
]}
...
`

10. 发布站点脚本 & CNAME 文件

运行如下命令:

gatsby build

待发布的站点就在 public 目录里了

为了发布方便做了个发布脚本 deploy.sh

#!/bin/bash
rm -rf ./.cache
rm -rf ./public/*

gatsby build

echo ""
echo "====>> publish version: $1 <<===="
echo ""

cd ./public
git add .
git commit -m "$1"
git push

注:发布前最好清理一下 public 目录,因为总是增量发布,所以会有很多历史文件,同一个功能只是 hash 有变化。

配置 package.json

...
"scripts": {
	...
	"deploy": "sh deploy.sh"
	...
}
...

使用方法如下:

npm run deploy -- v0.0.1

另:如果自己有独立域名,还需要将 CNAME 文件放入发布目录。

只需要将 CNAME 文件放入 static/ 目录即可,脚本编译的时候会自动同步过去。


- THE END -