写在前面:
用烦了wordpress,hexo和其他各种各样的博客框架,是时候该从桎梏中跳出,写一个自己的博客了!我的博客 www.panyunyi.cn
博客的github地址 https://github.com/panyunyi97/react-blog
欢迎 star & pr
在试图搭建一个完全属于自己且自主可控的博客之前,我使用过 wordpress 和 Hexo 等傻瓜式的博客。但这类可少量客制化的博客并不能完完全全满足我的需求,我希望我自己的博客在我手中完全可控,一丝一毫都可以被我掌握,这样才显得安心和舒适。作为一个开发者,自己的博客其实就是自己的Resume,不单单需要内容翔实,更需要样式美观可控,所以我建议大家,都自己动手搭建一个真正属于自己的博客!
在启动之前我也雄心壮志过,试图从零开始一点一点做,后来发现,成本太高而且旷日持久,所以我决定偷师github,找一个符合自己审美的React-blog进行深度改造,非常幸运的是,我找到了一个完全可用的React Blog。
基于这个原始版blog,我开启了我的blog 客制化之路。
首先给大家看一下最后的成果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BTGbZlAx-1607693346403)(https://s3.ax1x.com/2020/12/11/rAgKEV.png)]
以及实现的功能一览:
实现功能
- 前台:主页 + 列表页 + 搜索页 + 分类页 + 标签页
- 后台:文章管理 + 用户管理
- 响应式、文章锚点导航、回到顶部、
markdown
代码高亮、mathjax
支持 - 用户:站内用户、
github
第三方授权登录的用户 - 用户可以评论与回复、以及邮件通知回复的状态
-
md
文件导入导出功能!可以直接上传md
文件生成文章 - 私密与公开文章分离
- 一键评论,无需注册
- 主页背景
- 文章置顶
- 闲言碎语
技术栈
-
前端 (基于
create-react-app eject
后的配置)- react v16.9.0
hooks
+redux
+react-router4
marked highlight.js
webpack
打包优化axios
封装
- react v16.9.0
-
后端 (自构建后台项目)
koa2
+koa-router
sequelize
+mysql
jwt
+bcrypt
nodemailer
koa-send
archiver
项目结构
.
│
├─config // 构建配置
├─public // html 入口
├─scripts // 项目脚本
└─server // 后端
├─config // 项目配置 github、email、database、token-secret 等等
├─controllers // 业务控制层
├─middlewares // 中间件
├─models // 数据库模型
├─router // 路由
├─utils // 工具包
├─ app.js // 后端主入口文件
├─ initData.js // 初始化基础数据脚本
└─...
│
└─src // 前端项目源码
├─assets // 静态文件
├─components // 公用组件
├─layout // 布局组件
├─redux // redux 目录
├─routes // 路由
├─styles // 样式
├─utils // 工具包
├─views // 视图层
├─ App.jsx // 后端主入口文件
├─ config.js // 项目配置 github 个人主页、个人介绍等等
├─ index.js // 主入口文件
└─...
blog改造流程一览
- 页面的客制化改造
- 功能的修复与添加
- 个人信息的填充
- 小彩蛋
页面的客制化改造
为了更加适用于自己的使用场景,对页面进行了一些客制化的改造
-
页面背景(写成了可配置可拆卸的)
此处奉上我最喜欢的这张图即封面图 -
页面动效
加上了很多博客都有的翻转滚动的小棒!是不是很棒!
功能的修复与添加
为了将博客所有者与阅读者隔离开,加入了role字段进行控制。
-
公开与私密文章:
博客不单单是展示自己、记录笔记的地方,更是个人情感的树洞,是回忆编织的大网,所以肯定是需要将公开和私密的文章隔离开啦,根据用户角色鉴权来返回对应的数据。这样更好的保护了隐私,也让博客所有者穿上了自己的底裤~
-
Markdown 编辑器 mathjax 支持
因为博主是一个弱小的科研爱好者,所以写公式更是必须的啦,之前找的这个React-blog在这个功能上存在一些瑕疵,不能有效的支持mathjax,所以进行了一些改动,对 react-simplemde-editor 进行了一些简单的封装,使其支持MathJax预览,在文章显示上适用marked进行编码,使mathjax可以有效显示。
具体实现
export const translateMarkdown2html = (plainText, isGuardXss = false) => {
const marked_render = new marked.Renderer()
marked_render.old_paragraph = marked_render.paragraph
// 重写`paragraph()`方法
marked_render.paragraph = function(text) {
// isTeXInline - 该文本是否有行内公式
var isTeXInline = /\$(.*)\$/g.test(text)
// isTeXLine - 该文本是否有行间公式
var isTeXLine = /^\$\$(\s*.*\s*)\$\$$/.test(text)
if (!isTeXLine && isTeXInline) {
// 如果不是行间公式,但是行内公式,则使用<span class="marked_inline_tex">包裹公式内容,消除$定界符
text = text.replace(/(\$([^\$]*)\$)+/g, function($1, $2) {
// 避免和行内代码冲突
if ($2.indexOf('<code>') >= 0 || $2.indexOf('</code>') >= 0) {
return $2
} else {
return '<span class=\'marked_inline_tex\'>' + $2.replace(/\$/g, '') + '</span>'
}
})
} else {
// 如果是行间公式,则使用<div class='marked_tex'>包裹公式内容,消除$$定界符
// 如果不是LaTex公式,则直接返回原文本
text = (isTeXLine) ? '<div class=\'marked_tex\'>' + text.replace(/\$/g, '') + '</div>' : text
}
// 使用渲染器原有的`paragraph()`方法渲染整段文本
text = this.old_paragraph(text)
return text
}
// 配置marked.js的渲染器为marked_render,使用highlight.js来自动高亮MarkDown中的代码
return marked(isGuardXss ? xss(plainText) : plainText, {
renderer: marked_render,
pedantic: false,
gfm: true,
tables: true,
breaks: false,
sanitize: false,
smartLists: true,
smartypants: false,
xhtml: false,
highlight: function(code) {
/*eslint no-undef: "off"*/
return hljs.highlightAuto(code).value
}
})
}
-
文章置顶
置顶文章是一个必须的功能,当你有一些得意之作时,更希望它能有更高的优先级,能帮助到更多的人,所以增加了该功能
-
一键评论
这是为了方便朋友们能够更快速的进行评论,因为让朋友们注册的话有持有密码和密码泄漏的风险,所以直接使用用户名和qq邮箱(方便拿到头像)进行快速评论即可,后台将自动注册登录该用户。
-
github登录:
之前的blog中的登录方式已经被取缔了,所以更新了新的登录方式:
具体可见:github oauth2 认证
-
闲言碎语:
该模块是模拟信息流模式留给博客所有者的独立空间,可以发布心情和感受,更好地完善博客的完整功能————记录美好生活。
-
后台管理:
个人信息的填充
把很多东西都做成了完全可配置的
例如: 友链、头像等等,如果你不喜欢折腾,可以即插即用,部署好mysql/npm/yarn 就可以一键使用了,未来也会提供一键安装的功能。
import React from 'react'
import { Icon } from 'antd'
import SvgIcon from '@/components/SvgIcon'
import Href from '@/components/Href'
import MyInfo from '@/views/web/about/MyInfo'
// API_BASE_URL
export const API_BASE_URL = 'http://120.79.229.207:6060'
// export const API_BASE_URL = 'http://127.0.0.1:6060'
// project config
export const HEADER_BLOG_NAME = '菜园子' // header title 显示的名字
// === sidebar
export const SIDEBAR = {
avatar: require('@/assets/images/avatar.jpeg'), // 侧边栏头像
title: '种菜的小朋友', // 标题
subTitle: 'Carpe diem', // 子标题
// 个人主页
homepages: {
github: {
link: 'https://github.com/panyunyi97',
icon: <Icon type='github' theme='filled' className='homepage-icon' />
},
juejin: {
link: 'https://juejin.im/user/96412755827687',
icon: <SvgIcon type='iconjuejin' className='homepage-icon' />
}
},
friendslink: {
lizi: {
link: 'http://blog.liziyang.co/',
img: 'http://blog.liziyang.co/images/pikachu.jpg',
},
wizchen: {
link: 'http://blog.wizchen.com',
img: 'https://cdn.jsdelivr.net/gh/wizcheu/content1@main/img/header.gif'
}
}
}
// === discuss avatar
export const DISCUSS_AVATAR = SIDEBAR.avatar // 评论框博主头像
/**
* github config
*/
export const GITHUB = {
enable: true, // github 第三方授权开关
client_id: '87a4f88b943adaafd44a', // Setting > Developer setting > OAuth applications => client_id
url: 'https://github.com/login/oauth/authorize' // 跳转的登录的地址
}
export const ABOUT = {
avatar: SIDEBAR.avatar,
describe: SIDEBAR.subTitle,
discuss: true, // 关于页面是否开启讨论
renderMyInfo: <MyInfo /> // 我的介绍 自定义组件 => src/views/web/about/MyInfo.jsx
}
// 公告 announcement
export const ANNOUNCEMENT = {
enable: true, // 是否开启
content: (
<>
个人笔记网站,请访问
<Href href='https://www.yuque.com/zhongcaidexiaopengyou/kb'> panyunyi's note</Href>
</>
)
}
小彩蛋:
作者本人也只是前端初学者,所以有些地方写的不甚严谨,还希望大家多多包涵,多提issue,多提pr!
写在后面
再次感谢alvin0216的react-blog,我只是在前人的肩膀上摘苹果。
大概的功能就是这些,已经完全能够cover日常的工作和分享需求,支持线上的markdown编辑和导入导出。
希望大家都能早日拥有属于自己的博客!
欢迎 star & pr
项目地址是 React-blog