Watcher

构建一个Blog

2019-12-13

这个 Blog 我选了一个我很不熟悉的技术栈 Gatsby,这应该算是就是跳出舒适圈吧。

002

先说说我的诉求,我希望我的 Blog 可控性比较高,这样方便我做一些自己想做的事情。然后我希望它尽可能的简洁跟简单,个人有点极简主义。然后我希望 Blog 的内容使用 MarkDown 编写,最后我希望它可以自动部署,我 push 到 Github 就可以自动发布。

好吧,就这些,让我们开始吧。

基础架构

Gatsby 是一个很优秀的基于 React 的开源架构,使用它,你可以很简单很快速构建你的网站,它已经帮你做了很多事情,比如静态化页面、统一的数据获取方式等。我很久前就有学习它的计划,但一直搁置,这次我选择使用它。

使用 Gatsby 跟我使用之前的 React 架构最大的区别应该是在数据资源层上,Gatsby 有一个内置的数据层,是基于 GraphQL 的,这个数据层包含了你开发过程需要的所有资源,比如,图片、视频、JSON 数据等。GraphQL 是一种查询语言,我在几年前就开始关注,但因为使用起来需要涉及到服务端的改造(原来的理解,但现在似乎也可以独立在展示层使用),就一直停留在只会基础的使用阶段,没有深入了解,我计划会在另一篇博客中在讲讲我的理解,这里就不明细说了。

Markdown

使用 Gatsby 开发 Blog 系统,markdown 文件也是一种静态资源,我们需要使用 Gatsby 的插件把 markdown 文件加载到 Gatsby 的数据层,然后在应用就可以随意的使用了。

module.exports = {
  plugins: [
    {
      resolve: "gatsby-source-filesystem",      options: {
        name: "content",
        path: `${__dirname}/src/content`,
      },
    },
    "gatsby-transformer-remark",  ],
}

我们先使用 gatsby-source-filesystem 插件把我们 markdown 文件添加到系统内,然后使用 gatsby-transformer-remark 插件把刚刚添加的内容转换为我们可以使用的数据。

当你启动了 Gatsby 的开发模式,它会自同时给你启动一个 graphql 服务,可以通过http://localhost:8001/___graphql来访问。在使用了 gatsby-transformer-remark 插件后,你会发现你的数据源里多了 allMarkdownRemarkmarkdownRemark,一个是用来查询所有的 markdown 数据,一个是来获取单个 markdown 数据的详情

Gatsby 提供了很多类似生命周期的接口,你可以使用这些接口来完成你的业务,比如在我们这个场景下,我们要使用 Gatsby 的创建页面的接口,因为我的诉求是最终通过 markdown 的方式来写 blog,那么在我每次创建一个新的 markdown 文件后,都需要把这个文件转换为一个页面。

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
  const result = await graphql(`
    query MyQuery {
      allMarkdownRemark {
        edges {
          node {
            frontmatter {
              path
            }
          }
        }
      }
    }
  `)
  result.data.allMarkdownRemark.edges.forEach(edge => {
    const { node } = edge
    createPage({      path: node.frontmatter.path,      component: path.resolve(`src/templates/post.js`),    })  })
}

上面的代码就是使用 Gatsby 给我们提供 graphql,来查询到所有的 markdown 数据,然后通过 createPage 方法创建对应的页面,这样我们就可以通过路由来访问我们的页面了,这里需要注意,在创建页面的时候,我们还需要指定一个模板文件,每个页面的具体内容,我们需要在模板里再通过 markdownRemark 去查询,查询条件就是路由 path,如下所示:

export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "YYYY MM DD")
        title
        path
      }
    }
  }
`

然后我们就可以写我们最熟悉的组件代码了

const Templates = props => {
  const { markdownRemark } = props.data
  const { html, frontmatter } = markdownRemark
  return (
    <div>
      <h1>{frontmatter.title}</h1>
      <h2>{frontmatter.date}</h2>
      <div dangerouslySetInnerHTML={{ __html: html }} />
    </div>
  )
}

自动发布

开始处理自动发布需求,虽然网上有很多类似的架构跟教程,但我觉得既然底层原理很简单,还是自己实现一个,这样比较好控制。

自动发布的核心是 Git 的 Hook 机制,在 Github 里对应的是 Settings 页面的 Webhooks 选项,可以让用户填写一个 API,在你的库发生改变的时候就自动调用里设置的 API。

然后我用 node 的 Express 在服务端运行了一个 Web 程序,通过 shelljs 这个库来执行一个写好的脚本,在脚本里做安装依赖、编译、重启 Nginx 的操作,这样看起来一个简单的自动化发布系统就做好了,虽然很简陋。

总结

到目前为止,我的需求都已经满足了,一个干净的用 Markdown 编写的可以自动发布的 Blog 系统。其实还有很多细节跟踩坑我没细说,主要是因为我不太会把控技术细节跟描述文字之间的界线,担心太多的技术内容导致阅读体验下降。

我比较喜欢兴趣式学习方式,所以我也用这种方式来学习前端技术,就像你看到的这个 Blog,因为兴趣,我在这个过程中学习了使用 Gatsby,学习了自动发布等工作中会遇到的一些技术,我很喜欢这种方式跟节奏。

在这里讨论


Yang

Yang的个人博客
我在这里记录我的生活