用了 Hexo 大概有一年多了,越用越觉得这个东西比较臃肿,不可控,构建速度慢,所以前段时间一直在寻找其他的博客框架。目前的博客从原理大致上可以分为两种

  • 动态博客 Ghost Wordpress
  • 静态博客 Hugo Hexo GatsbyJS VuePress Jekyll

动态博客的优势在于开箱即用,动态编辑,静态博客相对而言对服务器的依赖较低(可以依托于一些静态托管服务),二次开发相对容易。

本来其实我是想上Ghost的,但看上去社区反响并不是特别好(特别是近期的版本更新让用户似乎不是很满意),但是我又喜欢上了Ghost的主题。最终选择了GatsByJS作为我的最终博客框架。

所以接下来我会分享一下使用经验,并介绍一下我的迁移过程。

GatsByJS

GatsByJS 是一个基于 React 的静态网站生成器,理论上你可以用他写一切静态网站,博客自然不在话下。目前社区非常活跃,大家用他写在线文档,博客相对较多些。

在经过一段时间的考察后,我认为GatsByJ具有如下优势

  • 活跃的社区
  • 享受 React 带来的成熟技术栈
  • 平滑的学习曲线
  • 构建速度与最终访问都非常快
  • 更好的错误提示(hexo提供的错误很多时候几乎不可读)

如果你是一名前端开发工程师,那么这个东西可能可以最方便的构建你心目中的博客。

起步

在构建博客的过程中,我推荐找一个 Github 上的开源模板来进行二次开发,阅读源码能加快你的上手速度。

我这里使用的是 gatsby-casper 作为项目起始,他基于 Ghost 主题风格。

你当然也可以使用一个更为基础的起步模板gatsby-starter-blog

在找到一个 BlogStar 项目后,你可以去大致阅读一下源码来了解GatsByJS的工作原理。我这里指出一些可能会对你有帮助的内容

  • gatsby 使用了 GraphiQL 作为一种数据查询语言,有点类似于 JSON,具有一定的可视化能力
  • gatsby-config.js 包含了一些基本配置 和 项目插件
  • gatsby-node.js 包含了项目需要的数据源,项目内部的详情页
  • src/pages 包含了项目的主要页面
  • 通常来讲 详情页需要在 gatsby-node.js 里面利用 CreatePageAPI 进行注册 主页在 Pages 里面直接增加
  • 不要用普通组件(带 State 的那种)的思路去开发,全部用函数式组件,因为其静态生成的特性,页面不应该拥有状态,你在生命周期里做的操作也只是在 DEV 模式会生效

到这一步,你应该对GatsByJS有了初步的了解。接下来要做的就是进行文章迁移

迁移

Hexo 本身也是一个静态网站生成器,所以迁徙起来并不是非常麻烦,大致就是一些文章头部数据的重新格式化,我这个起始模板需要增加一些属性,所以我写了个一个简单的node.js脚本来帮助我进行转换。

const fs = require('fs');
const readLine = require('readline');
const path = require('path');

let posts = fs.readdirSync('./posts');
let imageCount = 1;

function transformPost(postPath, exportsPath) {
  return new Promise((resolve, reject) => {
    let arr = [];
    let reader = readLine.createInterface({
      input: fs.createReadStream(postPath),
    });
    reader.on('line', line => {
      if (line.indexOf('<!-- more -->') > -1) return;
      arr.push(line);
    });
    reader.on('close', function() {
      arr.splice(1, 0, 'author: Yinode');
      arr.splice(1, 0, `image: img/hexo/hexo-count-${imageCount++}.jpg`);
      arr.splice(1, 0, 'draft: false');
      let fileContent = arr.join('\n');
      let writeStream = fs.createWriteStream(exportsPath);
      writeStream.write(fileContent);
      resolve(1);
    });
  });
}

posts.forEach(async fileName => {
  await transformPost(
    path.resolve(__dirname, 'posts', fileName),
    path.resolve(__dirname, 'exports', fileName),
  );
});

这一部分并不是必须的,要根据你具体采用的起始模板而定。可以看到由于 Markdown 的通用特性,我们可以非常方便的转移我们的文章。

改造

现在我们拥有了可以使用的文章,下一步就是建立我们的页面。我个人比较需要的页面大致如下

主页

  1. 主页(只显示 30 篇文章)
  2. 文章列表页(所有文章)
  3. 标签页
  4. 归档页
  5. 关于

详情页

  1. 标签详情页 显示该标签下的所有文章

主页都在src/pages下构建,详情页你可以放在src/template下,组件(包含元件,布局组件)放置在src/components下。

增加统计功能

由于其静态生成的特性,所以我们需要一种方式来进行页面的访问统计,我这里采用Koa建立一个简单的服务器,并配合iframe插入到页面之中,每访问一次,就进行计数自增。

koa-blog-visit-count

增加 LiveRe

需要对官方的代码进行拆解,分为两步

库放置到文章模板的

<script src="https://cdn-city.livere.com/js/embed.dist.js" async>
</script>

在文章的底部 增加评论容器

  <div id="lv-container" data-id="city" data-uid="MTAyMC8zMzA3Mi85NjM0">
  </div>

部署

之前一直都采用travis-ci的持续构建服务 + gh-pages 双分支(source 为源,master 为静态文件) + VPS 自动 pull 的方案。但事实上还是访问比较慢,并且整个过程过于麻烦了点。

这次换成netlify的自动构建+部署服务,可以做到完美托管,不需要写任何脚本,访问速度应该来讲比国内的服务器肯定忙,但是比 github 略快,服务很不错。推荐!

new

之前一直 hexo new 来着, 现在当然也要写个小脚本来帮助我们进行新文章的初始化辣

const fs = require('fs');
const path = require('path');
const dayjs = require('dayjs');

const postName = process.argv[2];
const postPath = path.resolve(__dirname, 'src/content/', `${postName}.md`);

const writeStream = fs.createWriteStream(postPath);
writeStream.write(`---
draft: false
image: img/hexo-wa.jpg
author: Yinode
title: ${postName}
date: ${dayjs().format('YYYY-MM-DD HH:mm:ss')}
tags: []
---
`);

添加到package.json

{
  "scripts": {
    "new": "node new.js"
  }
}

接下来,在命令行中

yarn new "这是一篇新文章"

总结

很俊,很好玩,GatsByJS的可玩性真的好玩,相当推荐尝试。

这里给出我的博客源代码地址

zhangzhengyi12.github.io