概述
来源
该项目是技术胖的一个项目,涉及了前台中台后台,觉得是个不错的练手项目.
概述
博客前台: React Hooks + Next.js + marked + highlight + Ant Design
数据中台: Egg.js(阿里的一个基于 Koa2 的框架) + MySQL
管理后台: React Hooks + Ant Design (不用 Next.js)
Next.js 是一个轻量级的 React 服务端渲染应用框架。有了它我们可以简单轻松的实现 React 的服务端渲染,从而加快首屏打开速度,也可以作 SEO(搜索引擎优化了)。并且路由,webpack 配置等也是框架配置好的。
个人看法:配置太全了,自己写着玩的项目还好,企业级的项目用起来可能不够灵活。
前台
布局
组件内的布局基本都是用 Antd 的 栅格-响应式布局 ,<Col>
的 xs,sm 等这些属性本质上是媒介查询;给 <Row>
添加属性实现 flex 布局 type="flex" justify="center"
组件一般都是用 Antd 的组件组合而成
请求数据
数据来源于请求中台接口
函数组件的 getInitialProps
方法写一个 promise 并返回 promise 的结果(Next.js 框架)
文档
该方法返回的应该是一个普通的 JS 对象;不能用于子组件中,只能用于 pages 页面组件中;该方法只会加载在服务端,所以在该方法中 console 是不会显示的;
该方法用于一开始就请求一次的数据(之后不用请求了),然后 promise 的 resolve 的结果这个对象,会变成函数组件的 props 对象。例子:
1 | // 函数组件 |
这种写法其实和写在最普通的 useEffect 的写法效果一样
Hook
跨域
中台安装 egg-cors ,并做相应配置。本质是设置 Access-Control-Allow-Origin
跨域白名单
Egg.js 的跨域不支持白名单,只支持单个域名,这样前后台项目不能一起跑。解决方案是需要自己写一个中间件实现白名单,这是别人写的中间件,Github,这里直接用了这个中间件
最后线上版本舍弃了这个插件,见博客项目上线部署博文的 跨域
涉及 id 的页面
像不同种类的列表页,文章详情页这种涉及 id 的页面,路由设置 和 数据获取 稍微复杂一些
例如,列表页中每篇文章的标题,是一个带 id 的 <Link>
1 | <div className="list-title"> |
点击后跳转带详情页,所以详情页的 getInitialProps
获取数据的时候是根据路由的 id 去 post
1 | Detailed.getInitialProps = async context => { |
对应的接口则是由文章的 id 在数据库查询
1 | async getArticleById() { |
PS:前端在路由跳转的时候(Router.push()
或 <Link href=‘xx’>
),路由地址最后得是 xx?id=1
这样,而不是 xx/?id=1
。但是后端在配置接口的时候依旧是 xx/:id
详情页 中对 Markdown 语法的解析
把从数据库取出来的 markdown 语法的字符串,编译成正常的文章并显示样式,是用 marked + heighlight.js
详情页 中的 导航目录组件
效果如下所示:
把这个组件固定在页面右侧是用了 Antd 的 Affix 固钉 组件,Anchor 锚点
解析文章内容生成目录是用了阿里的一个插件 tocify.tsx
中台
概况 & RESTful 规范
配置了两套路由,/default/
开头的用于前台,/admin/
开头的用于后台
所有数据的获得和业务逻辑的操作都是通过中台实现的,也就是说中台只提供接口,这里的设计我们采用 RESTful 的规则,让 egg 为前端提供 Api 接口,实现中台主要的功能
RESTful 是目前最流行的网络应用程序设计风格和开发方式,大量使用在移动端 App 上和前后端分离的接口设计。这种形式更直观并且接口也有了一定的约束性。
约束的请求方式和对应的操作:
- GET(SELECT) : 从服务端取出资源,可以同时取出一项或者多项。
- POST(CREATE) :在服务器新建一个资源。
- PUT(UPDATE) :在服务器更新资源(客户端提供改变后的完整资源)。
- DELETE(DELETE) :从服务器删除资源。
数据库
数据库采用的是关系型数据库 MySQL,安装了官方带的 WorkBench 对数据库进行可视化管理。把 Egg.js 项目进行连接数据库的配置后即能使用
数据库设计
表 和 Cloum 如下:
type
- id : 类型编号 int 类型
- typeName: 文章类型名称 varchar 类型
- orderNum: 类型排序编号 int 类型
article
- id : 文章编号 int 类型
- type_id : 文章类型编号 int 类型
- title : 文章标题,varchar 类型
- article_cointent : 文章主体内容,text 类型, TEXT(n),这里的 n 是文本长度,可以设置一个较大值
- introduce: 文章简介,text 类型
- addTime : 文章发布时间,int(11)类型
- view_count :浏览次数, int 类型
admin_user
- id
- userName
- password
数据库语法
常用例子
- 增,直接对某个表进行 insert
- 删,直接根据 id,对某个表进行 delete
- 改,直接对某个表进行 udate
- 查,eg:
1 | 'SELECT article.id as id, article.title as title, article.article_content as article_content, FROM_UNIXTIME(article.addTime,'%Y-%m-%d' ) as addTime, type.typeName as typeName, FROM article LEFT JOIN type ON article.type_id = type.Id ORDER BY article.addTime DESC'; // DESC是逆序的意思,ASC是正序` |
后台
直接用 React + Antd 写的,不用 Next.js
React 项目中的根目录的 index.js 应该是渲染一个路由组件(自己配的),由这个路由组件再去加载其他页面组件。例如路由组件可以如下:
1 | import React from 'react' |
登录页面
样式
样式上比较简单,是由 Antd 的 Card, Input, Icon, Button, Spin, message 组件写的
逻辑
目前没有 注册 这个操作,用户名密码及 id 是采用直接在 workbench 插入新数据的方式注册
点击登录按钮的时候,向接口 post 用户名和密码,向数据库查询这个用户名和密码。如果登录成功就返回登录成功的信息和一个 token,组件就把 token 存到 localstorage 中(localStorage.setItem('openId', res.data.openId)
),并跳转到首页(props.history.push('/index')
)
附上接口代码:
1 | // 判断用户名密码是否正确 |
路由守卫
登录后,我们生成了 session,通过后台是不是存在对应的 session,作一个中台的路由守卫。如果没有登录,是不允许访问后台对应的接口,也没办法作对应的操作。这样就实现了接口的安全
其实就是路由守卫,没有 token 的时候不让访问接口(等同于不让访问某些页面)。在这里我们是通过 egg.js 的中间件实现的(详情版见教程)
- 写一个中间件文件,并应用在中台路由中 (目前只有
getTypeInfo
这个接口有守卫
1 | module.exports = options => { |
1 | // 中台路由:对某个接口进行路由守卫 |
- 在正常情况下前后台是不能共享
session
的,需要要在 egg 端的/config/config.default.js
里的config.cor
配置项增加credentials:true
;并且前台的请求中,需要带withCredentials: true
。
管理页
布局上用 Antd 的 layout-侧边布局。再做相应的路由配置就能分页
管理页之添加文章
直接在 编辑文章 分页进行编辑并保存
前端:把文章的数据都打包好之后 POST 给后端接口
后端:数据库对表进行插入数据的操作。id 方面是先找到最大 id,然后让新文章的 id 等于最大值+1
管理页之修改文章
在 文章列表 分页,点击某篇文章的 修改 按钮,就会进行路由跳转到 编辑文章 分页并附上文章 id 保存在路由中
所以在 编辑文章 分页还有这样一个逻辑:有一个getArticleById
方法,会根据 id 向接口请求对应的文章的数据并显示在页面上,这个方法会在 useEffect 中执行让它在页面刚刷新的时候就执行(如果路由有带 id 的话)。此外,改页面的 发布文章 这个按钮也会判断是否有 id,无的话就是请求发布文章的接口,有的话就是请求更新文章的接口
项目优化计划
前台
- markdown 语法中图片的解析和存储
markdown 语法中图片是直接写成![](/img/二叉树的遍历/二叉树的遍历_1.png)
这种形式,完全不做配置的话会出现下图这种 404 的情况 - 文章简介的实现
- 访问人数的实现。目前的访问人数是随机生成的
- UI 美化
后台
- 登录账号和密码加密
- 实现手机号注册?或者说这种管理中台一般是管理者才能使用,管理者账号直接写在数据库就行了,不必要有注册功能
- 页面 UI 优化,使该系统美观一些
- 添加文章能插入图片
上线及部署
见另一篇文章
参考资料
技术胖博客