基于Github issues + umi 搭建一个免费的带评论功能的博客(二)

1,545 阅读8分钟

上一篇文章我主要介绍了什么是Github App,以及如何利用GitHub App为我们的repository进行授权,解决了博客的数据存储和获取,那么这篇文章我将着重介绍博客搭建过程中用到的前端技术。

博客地址

🚗为什么是Umi

用过React的同学应该很多人都知道Umi(乌米)这个框架,没听过的那么我也建议大家有时间可以去了解下它传送门

Umi内置了reactpreactwebpackreact-routerbabel 等,可以做到开箱即用,它独特的约定式路由可以帮我们省去路由配置的步骤。所以使用Umi脚手架新建的工程,目录结构式非常清晰明了的。下面看下一个Umi创建的工程的目录结构:

.
├── dist/                          // 默认的 build 输出目录
├── mock/                          // mock 文件所在目录,基于 express
├── config/
    ├── config.js                  // umi 配置,同 .umirc.js,二选一
└── src/                           // 源码目录,可选
    ├── layouts/index.js           // 全局布局
    ├── pages/                     // 页面目录,里面的文件即路由
        ├── .umi/                  // dev 临时目录,需添加到 .gitignore
        ├── .umi-production/       // build 临时目录,会自动删除
        ├── document.ejs           // HTML 模板
        ├── 404.js                 // 404 页面
        ├── page1.js               // 页面 1,任意命名,导出 react 组件
        ├── page1.test.js          // 用例文件,umi test 会匹配所有 .test.js 和 .e2e.js 结尾的文件
        └── page2.js               // 页面 2,任意命名
    ├── global.css                 // 约定的全局样式文件,自动引入,也可以用 global.less
    ├── global.js                  // 可以在这里加入 polyfill
    ├── app.js                     // 运行时配置文件
├── .umirc.js                      // umi 配置,同 config/config.js,二选一
├── .env                           // 环境变量
└── package.json

🚢 markdown

github issues是支持markdown格式的,因此我们博客文章的展示必须是要支持markdown格式,这里我选择了react-markdown

react-markdown的使用

首先需要安装:

npm i react-markdown

react-markdown默认是不支持代码语法高亮提示的,因此,还需要安装react-syntax-highlighter这个库:

npm i react-syntax-highlighter

因为我们的博客是基于Githubmarkdown来书写的,因此和react-markdown自带的markdown样式还是有些差距的,这里我还引入了github-markdown-css这个库来解决样式渲染的问题。

为了使用的方便,封装了一个markdown组件:

// index.js
import ReactMarkdown from 'react-markdown';
import CodeBlock from '@/components/markdown/codeBlock';
import 'github-markdown-css';
import './index.less';

export default (props) => {
  const { dataSource } = props;
  return (
    <ReactMarkdown
      escapeHtml={false}
      renderers={{
        code: CodeBlock,
      }}
      className="markdown-body"
      source={dataSource}
    />
  )
}

代码高亮:

// codeBlock.js
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
// 设置高亮样式
import { solarizedlight } from "react-syntax-highlighter/dist/esm/styles/prism";
// 设置高亮的语言
import { jsx, javascript, sass, scss, less, css } from "react-syntax-highlighter/dist/esm/languages/prism";

class CodeBlock extends PureComponent {
  static propTypes = {
    value: PropTypes.string.isRequired,
    language: PropTypes.string
  };

  static defaultProps = {
    language: null
  };

  componentDidMount() {
    // 注册要高亮的语法,
    // 注意:如果不设置打包后供第三方使用是不起作用的
    SyntaxHighlighter.registerLanguage("jsx", jsx);
    SyntaxHighlighter.registerLanguage("javascript", javascript);
    SyntaxHighlighter.registerLanguage("sass", sass);
    SyntaxHighlighter.registerLanguage("scss", scss);
    SyntaxHighlighter.registerLanguage("less", less);
    SyntaxHighlighter.registerLanguage("css", css);
  }

  render() {
    const { language, value } = this.props;
    return (
      <figure className="highlight">
        <SyntaxHighlighter language={language} style={solarizedlight}>
          {value}
        </SyntaxHighlighter>
      </figure>
    );
  }
}

export default CodeBlock;

关于代码高亮显示需要注意的是,我们必须要使用registerLanguage方法来注册你想要高亮显示的语言,同时react-syntax-highlighter提供了若干种代码高亮的样式供我们使用,在react-syntax-highlighter/dist/esm/styles/prism目录下可以选择你喜欢的代码高亮样式,这里我选择了solarizedlight这款样式。

🦁️关于路由方式的选择

我们知道,react有三种路由方式:history路由hash路由memory路由,常用的是前两种方式,我们的博客最终是要发布并部署到到github page上面的,如果选择history路由,那么部署上线后,在非根路径下刷新页面会报404错误。

单页应用一般是需要在服务端设置将所有的页面都重定向到index.html的,比如我们刷新http:xxx.com/list页面,服务器会去在根路径的list目录下去查找资源文件,这个文件服务器上显然是不存在的,这个时候就会返回404

遇到这个问题我们一般会选择在nginx上进行如下配置:

location /{
     root   /data/nginx/html;
     index  index.html index.htm;
     error_page 404 /index.html;
}

也就是说找不到对应资源的时候会自动重定向到index.html

但是很显然,在 github page上我们是无法这么操作的,因此这里我们就偷个懒,选择了hash路由。

umi上设置路由方式是很方便的,直接在根目录下的.umirc.js文件中进行如下配置即可:

export default {
  history: 'hash',
}

🌻关于前端跨域问题

前面我们说到,当我们在进行权限认证的时候,根据授权码向https://github.com/login/oauth/access_token这个地址进行请求,获取Token的时候,会存在跨域问题。那么有什么好的方式可以解决这个问题呢?

跨域产生的原因我就不阐述了,不清楚的同学可以去Google一下,这里我为了解决跨域问题,采用了cors方式,也就是对请求返回的header加上允许跨域操作的请求头。

思路大概是,认证的时候,向一个第三方代理接口发送认证请求,这个第三方代理接口再向github服务器发送真正的认证请求,这个第三方代理接口我们可以设置允许跨域的的headers。

关于zeito.co

那么现在的问题就很简单了,提供一个第三方认证的代理接口就可以解决我们的问题,为了践行文章的标题“免费”二字,专门为了这个接口去租一个服务器提供认证接口显然是得不偿失的,这里我向大家推荐zeit.co这个网站,他允许我们免费部署一个静态网站或者Serverless Functions。官网是这么介绍的:

ZEIT Now is a cloud platform for static sites and Serverless Functions. It enables developers to host websites and web services that deploy instantly, scale automatically, and requires no supervision, all with no configuration.

这里我们就是利用zeit.co提供的Serverless Functions功能,实现一个第三方的代理接口。

zeit.co提供了两种方式部署自己的服务。

  • 第一种方式是使用Now Cli工具来部署:
  1. 首先需要安装now cli工具。
npm i -g now
  1. 然后登录now
now login
  1. 创建自己的工程 这里可以根据自己的需要使用模版来创建自己的工程,或者直接使用已有的工程。
npm init next-app my-next-project

4, 发布自己的工程到zeit.co

now

这种方式简单、易用,但是也存在一个弊端,就是在第二步的时候可能受制于网络问题,出现无法登录的情况,当然如果你有梯子,这都不是事。如果你没有梯子,没关系,下面我介绍第二种方式来部署你的应用。

  • 使用GitHub部署你的应用 借助于Github也可以方便的部署你的应用,并且这种方式我觉得值得推荐,他有下面几个优点:
  1. Deploys every push in branches and pull requests to preview changes live
  2. Updates production domains with the most recent changes from the master branch
  3. Instant rollbacks for production domains when reverting changes

简单说就是每次在Github上面提交了代码,都会触发自动部署功能,并且会自动更新部署之后的域名。

我采用的是第二种方式来部署我的应用。下面介绍下具体的过程。

首先需要在zeito.co上注册一个账号,然后关联上你的Github账号,然后进入dashboard页面,这里就可以创建自己的应用,并且选择From Github中已存在的工程进行创建。

在这里插入图片描述

创建一个Serverless Functions

在根目录的api目录下创建一个date.js文件,比如:

// date.js
module.exports = (req, res) => {
  const date = new Date().toString();
  res.status(200).send(date);
};

当我们访问/api/date,接口就会返回当前的系统时间,也就是说,我们无需指定路由文件,每个文件就是一个独立的路由,这点有点类似于umi的约定式路由。

介绍完上面的Serverless Functions,现在回到我们的需求,创建一个第三方的代理接口,负责处理Github授权接口。

在api目录下新建githubAuth.js文件:

// githubAuth.js
require('es6-promise').polyfill();
require('isomorphic-fetch');

module.exports = async (req, res) => {
  // 设置请求头允许跨域
  res.setHeader("Access-Control-Allow-Origin", "*");

  const { query: { code } } = req;
  const clientID = '你的clientID';
  const clientSecret = '你的clientSecret';

  const url = 'https://github.com/login/oauth/access_token?' +
    `client_id=${clientID}&` +
    `client_secret=${clientSecret}&` +
    `code=${code}`;

  try{
    await fetch(url, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
      }
    }).then(response => {
      if(response.status === 200) {
        return response.json();
      }
    }).then(data => {
      res.json({
        data
      });
    })
  }
};

可以看到res.setHeader("Access-Control-Allow-Origin", "*")就是我们设置的允许跨域的Header。完整的代码大家可以参考这里

🌹部署你的博客

前端代码写完了就要考虑部署的问题了,这里我选择的是部署到Github Pages上,选择 github pages 的理由很简单:

  • 代码自动集成: github pages 集成在 github 中, 并且可以随着代码更新提交自动重新部署, 使用非常方便。
  • 提供免费的域名: 提供免费的 github.io 的域名, 免费部署你的静态网站,并且可以根据自己的需要配置自己的域名。
  • 无数量限制: github pages 没有使用的数量限制, 每一个 github repository 都可以部署为一个静态网站。

具体的使用和配置方法这里我就不在叙述了,大家可以自行Google,或者参考这里

下面的是我的公众号二维码图片,欢迎关注。

在这里插入图片描述