上一篇文章我主要介绍了什么是Github App,以及如何利用GitHub App为我们的repository进行授权,解决了博客的数据存储和获取,那么这篇文章我将着重介绍博客搭建过程中用到的前端技术。
?为什么是Umi
用过 React
的同学应该很多人都知道 Umi
(乌米)这个框架,没听过的那么我也建议大家有时间可以去了解下它传送门。
Umi
内置了 react
、preact
、webpack
、react-router
、babel
等,可以做到开箱即用,它独特的约定式路由可以帮我们省去路由配置的步骤。所以使用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
因为我们的博客是基于 Github
的 markdown
来书写的,因此和 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
服务器发送真正的认证请求,这个第三方代理接口我们可以设置允许跨域的的 header。
关于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 命令行工具来部署:
- 首先需要安装
now cli
工具。
npm i -g now
- 然后登录now
now login
- 创建自己的工程
这里可以根据自己的需要使用模版来创建自己的工程,或者直接使用已有的工程。
npm init next-app my-next-project
4, 发布自己的工程到zeit.co
now
这种方式简单、易用,但是也存在一个弊端,就是在第二步的时候可能受制于网络问题,出现无法登录的情况,当然如果你有梯子,这都不是事。如果你没有梯子,没关系,下面我介绍第二种方式来部署你的应用。
- 使用
GitHub
部署你的应用
借助于Github
也可以方便的部署你的应用,并且这种方式我觉得值得推荐,他有下面几个优点:
- Deploys every push in branches and pull requests to preview changes live
- Updates production domains with the most recent changes from the master branch
- 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
中, 并且可以随着代码更新提交自动重新部署, 使用非常方便。 - 提供免费的域名: 提供免费的
xxx.github.io
的域名, 免费部署你的静态网站,并且可以根据自己的需要配置自己的域名。 - 无数量限制:
Github Pages
没有使用的数量限制, 每一个Github repository
都可以部署为一个静态网站。
具体的使用和配置方法这里我就不在叙述了,大家可以自行 Google,或者参考这里。
最后给大家提供下我的博客地址,欢迎大家关注,评论留言。完整的代码在这里,欢迎大家star或者提出改进意见。
下面的是我的公众号二维码图片,欢迎关注。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。