Koa2-blog
2018-1-5 更新教程(新增上传头像、新增分页、样式改版、发布文章和评论支持markdown语法)
现在GitHub的代码结构有变,优化了蛮多东西
Node+Koa2+Mysql 搭建简易博客
预览地址
写在前面
本篇教程一方面是为了自己在学习的过程加深记忆,也是总结的过程。另一方面给对这方面不太了解的同学以借鉴经验。如发现问题还望指正,
如果你觉得这篇文章帮助到了你,那就赏脸给个star吧,https://github.com/wclimb/Koa2-blog
下一篇可能是 Node + express + mongoose 或 zepto源码系列
感谢您的阅读^_^
ps:关于markdown代码缩进问题,看起来不太舒服,但复制到编辑器是正常的哟!
演示效果
开发环境
- nodejs
v8.1.0
- koa
v2.3.0
- mysql
v5.7.0
准备工作
文中用到了promise、async await等语法,所以你需要一点es6的语法,传送门当然是阮老师的教程了 http://es6.ruanyifeng.com/
如果你已经配置好node和mysql可以跳过
经常会有人问报错的问题,运行出错大部分是因为不支持async,升级node版本可解决
|
|
下载mysql,并设置好用户名和密码,默认可以为用户名:root,密码:123456
|
|
然后开启mysql
输入密码之后创建database
(数据库),nodesql
是我们创建的数据库
记住sql语句后面一定要跟;
符号,接下来看看我们创建好的数据库列表
|
|
启用创建的数据库
显示Empty set (0.00 sec)
,因为我们还没有建表,稍后会用代码建表
注释:
这是后面建表之后的状态
目录结构
- config 存放默认文件
- lib 存放操作数据库文件
- middlewares 存放判断登录与否文件
- public 存放样式和头像文件
- routes 存放路由文件
- views 存放模板文件
- index 程序主文件
- package.json 包括项目名、作者、依赖等等
首先我们创建koa2-blog文件夹,然后cd koa2-blog
接着安装包,安装之前我们使用cnpm安装
|
|
各模块用处
koa node
框架koa-bodyparser
表单解析中间件koa-mysql-session
、koa-session-minimal
处理数据库的中间件koa-router
路由中间件koa-static
静态资源加载中间件ejs
模板引擎md5
密码加密moment
时间中间件mysql
数据库markdown-it
markdown语法koa-views
模板呈现中间件chai
mocha
测试使用koa-static-cache
文件缓存
在文件夹里面新建所需文件
首先配置config
我们新建default.js
文件
|
|
这是我们所需的一些字段,包括端口和数据库连接所需,最后我们把它exports暴露出去,以便可以在别的地方使用
配置index.js文件
index.js
|
|
我们使用koa-session-minimal``koa-mysql-session
来进行数据库的操作
使用koa-static
配置静态资源,目录设置为public
使用ejs
模板引擎
使用koa-bodyparser
来解析提交的表单信息
使用koa-router
做路由
使用koa-static-cache
来缓存文件
之前我们配置了default.js,我们就可以在这里使用了
首先引入进来 var config = require(‘./config/default.js’);
然后在数据库的操作的时候,如config.database.USERNAME,得到的就是root。
如果你觉得这篇文章帮助到了你,那就赏脸给个star吧,https://github.com/wclimb/Koa2-blog
配置lib的mysql.js文件
关于数据库的使用这里介绍一下,首先我们建立了数据库的连接池,以便后面的操作都可以使用到,我们创建了一个函数query
,通过返回promise的方式以便可以方便用.then()
来获取数据库返回的数据,然后我们定义了三个表的字段,通过createTable
来创建我们后面所需的三个表,包括posts(存储文章),users(存储用户),comment(存储评论),create table if not exists users()表示如果users表不存在则创建该表,避免每次重复建表报错的情况。后面我们定义了一系列的方法,最后把他们exports暴露出去。
这里只介绍注册用户insertData,后续的可以自行查看,都差不多
|
|
我们写了一个_sql的sql语句,意思是插入到users的表中(在这之前我们已经建立了users表)然后要插入的数据分别是name、pass、avator、moment,就是用户名、密码、头像、注册时间,最后调用query
函数把sql语句传进去(之前的写法是"insert into users(name,pass) values(?,?);"
,换成现在得更容易理解)
lib/mysql.js
下面是我们建的表
users | posts | comment |
---|---|---|
id | id | id |
name | name | name |
pass | title | content |
avator | content | moment |
moment | md | postid |
- | uid | avator |
- | moment | - |
- | comments | - |
- | pv | - |
- | avator | - |
- id主键递增
- name: 用户名
- pass:密码
- avator:头像
- title:文章标题
- content:文章内容和评论
- md:markdown语法
- uid:发表文章的用户id
- moment:创建时间
- comments:文章评论数
- pv:文章浏览数
- postid:文章id
现在感觉有点枯燥,那我们先来实现一下注册吧
实现注册页面
routers/singup.js
使用get方式得到’/signup’页面,然后渲染signup模板,这里我们还没有在写signup.ejs
views/signup.ejs
|
|
我们先安装supervisor
|
|
supervisor的作用是会监听文件的变化,而我们修改文件之后不必去重启程序
现在访问 localhost:3000/signup 看看效果吧。注意数据库一定要是开启的状态,不能关闭
完善注册功能
首先我们来完善一下样式吧,稍微美化一下
public/index.css
|
|
我们再把模板引擎的header和footer独立出来
/views/header.ejs
顺便引入index.css和jq
首先我们看到用到了session.user,这个值从哪来呢?请看下面的代码
我们可以看到我们向模板传了一个session值,session:ctx.session,这个值存取的就是用户的信息,包括用户名、登录之后的id等,session一般是你关闭浏览器就过期了,等于下次打开浏览器的时候就得重新登录了,用if判断他存不存在,就可以知道用户是否需要登录,如果不存在用户,则只显示全部文章
注册
登录
,如果session.user存在则有登出的按钮。
在上面我们会看到我用了另外一个if判断,判断type类型,这样做的目的是比如我们登录注册页面,注册页面的导航会高亮,其实就是添加了class:active;
之后我们每个ejs文件的头部会这样写<%- include("header",{type:'signup'}) %>
登录页面则是<%- include("header",{type:'signin'}) %>
/views/footer.ejs
修改views/signup.ejs
先看我们请求的url地址,是’/signup’,为什么是这个呢?我们看下面这段代码(后面有完整的)
我们的请求方式是post,地址是/signup
所以走了这段代码,之后会获取我们输入的信息,通过ctx.request.body拿到
这里重点就在于ajax提交了,提交之后换回数据 1 2 3,然后分别做正确错误处理,把信息写在error和success中
修改routers/signup.js
- 我们使用md5实现密码加密,长度是32位的
- 使用我们之前说的
bodyParse
来解析提交的数据,通过ctx.request.body
得到 - 我们引入了数据库的操作 findDataByName和insertData,因为之前我们在/lib/mysql.js中已经把他们写好,并暴露出来了。意思是先从数据库里面查找注册的用户名,如果找到了证明该用户名已经被注册过了,如果没有找到则使用insertData增加到数据库中
- ctx.body 是我们通过ajax提交之后给页面返回的数据,比如提交ajax成功之后
msg.data=1
的时候就代表用户存在,msg.data
出现在后面的signup.ejs
模板ajax请求中 - 上传头像之前要新建好文件夹,我们ajax发送的是base64的格式,然后使用fs.writeFile来生成图片
我们使用ajax来提交数据,方便来做成功错误的处理
模板引擎ejs
我们使用的是ejs,语法可以见ejs
之前我们写了这么一段代码
这里就用到了ejs所需的session 我们通过渲染signup.ejs模板,将值ctx.session赋值给session,之后我们就可以在signup.ejs中使用了
ejs的常用标签为:
<% code %>
:运行 JavaScript 代码,不输出<%= code %>
:显示转义后的 HTML内容<%- code %>
:显示原始 HTML 内容
<%= code %>
和<%- code %>
的区别在于,<%= code %>不管你写什么都会原样输出,比如code为 <strong>hello</strong>
的时候 <%= code %>
会显示<strong>hello</strong>
而<%- code %>
则显示加粗的hello
实现登录页面
修改 /routers/signin.js
修改 /views/signin.ejs
|
|
修改 index.js 文件 把下面这段代码注释去掉,之前注释是因为我们没有写signin的路由,以免报错,后面还有文章页和登出页的路由,大家记住一下
|
|
现在注册一下来看看效果吧
我们怎么查看我们注册好的账号和密码呢?打开mysql控制台
|
|
这样刚刚我们注册的用户信息都出现了
如果你觉得这篇文章帮助到了你,那就赏脸给个star吧,https://github.com/wclimb/Koa2-blog
登录页面
修改signin
routers/signin.js
我们进行登录操作,判断登录的用户名和密码是否有误,使用md5加密
我们可以看到登录成功返回的结果是result
结果是这样的一个json数组:id:4 name:rrr pass:…
[ RowDataPacket { id: 4, name: ‘rrr’, pass: ‘44f437ced647ec3f40fa0841041871cd’ } ]
修改views/signin.ejs
signin.ejs
我们增加了ajax请求,在routers/signin.js里,我们设置如果登录失败就返回false,登录成功返回true
|
|
那我们登录成功后要做跳转,可以看到window.location.href="/posts"
跳转到posts页面
全部文章
修改routers/posts.js
posts.js
修改 index.js
app.use(require(‘./routers/posts.js’).routes())的注释去掉
修改 views/posts.ejs
|
|
现在看看登录成功之后的页面吧
接下来我们实现登出页面
登出页面
修改 router/signout.js
signout.js
把session设置为null即可
修改 index.js
app.use(require(‘./routers/posts.js’).routes())的注释去掉,现在把注释的路由全部取消注释就对了
然后我们看看 views/header.ejs
我们点击登出后的ajax 的提交,成功之后回到posts页面
发表文章
修改router/posts
在后面增加
修改 views/create.ejs
create.ejs
现在看看能不能发表吧
即使我们发表了文章,但是当前我们的posts的页面没有显示,因为还没有获取到数据
我们可以看我们之前写的代码 router.get('/posts', async(ctx, next) => {}
路由
if前面这部分我们先不用管,后面会说。只需要看else后面的代码我们通过userModel.findPostByPage(1)
来获取第一页的数据,然后查找所有文章的数量,最后除以10拿到首页文章的页数,把数据postsPageLength
的值传给模板posts.ejs。这样就可以渲染出来了
修改 views/posts.ejs
posts.ejs
|
|
现在看看posts页面有没有文章出现了
我们看到是所第一页的文章数据,初始化的稍后我们是用服务端渲染的数据,使用了分页,每页显示10条数据,然后通过请求页数。
下面是服务端请求拿到的第一页的数据
要拿到别的页面数据的话要向服务器发送post请求,如下
单篇文章页面
但是我需要点击单篇文章的时候,显示一篇文章怎么办呢?
修改 routers/posts.js
在posts.js后面增加
|
|
现在的设计是,我们点进去出现的url是 /posts/1 这类的 1代表该篇文章的id,我们在数据库建表的时候就处理了,让id为主键,然后递增
我们通过userModel.findDataById 文章的id来查找数据库
我们通过userModel.findCommentById 文章的id来查找文章的评论,因为单篇文章里面有评论的功能
最后通过sPost.ejs模板渲染单篇文章
修改 views/sPost.ejs
sPost.ejs
|
|
现在点击单篇文章试试,进入单篇文章页面,但是编辑、删除、评论都还没有做,点击无效,我们先不做,先实现每个用户自己发表的文章列表,我们之前在 get ‘/posts’ 里面说先忽略if (ctx.request.querystring) {}里面的代码,这里是做了一个处理,假如用户点击了某个用户,该用户发表了几篇文章,我们需要只显示该用户发表的文章,那么进入的url应该是 /posts?author=xxx ,这个处理在posts.ejs 就已经加上了,就在文章的左下角,作者:xxx就是一个链接。我们通过判断用户来查找文章,继而有了ctx.request.querystring
获取到的是:author=xxx
注:这里我们处理了,通过判断 session.user === res['name']
如果不是该用户发的文章,不能编辑和删除,评论也是。这里面也可以注意一下包裹的<a href=""></a>
标签
个人已发表文章列表里面
还记得之前在 get ‘/post’ 里面的代码吗?
下面的代码就是之前说先不处理的代码片段,不过这个不用再次添加,之前已经添加好了,这段代码处理个人发布的文章列表,我们是通过selfPosts.ejs模板来渲染的,样式和全部文章列表一样,但是牵扯到分页的问题,
分页请求的是不同的接口地址
|
|
修改 selfPost.ejs
|
|
编辑文章、删除文章、评论、删除评论
评论
修改routers/posts.js
在post.js 后面增加
|
|
现在试试发表评论的功能吧,之所以这样简单,因为我们之前就在sPost.ejs做了好几个ajax的处理,删除文章和评论也是如此
评论我们也做了分页,所以后面会有一个评论的分页的接口,我们已经在sPost.ejs里面写好了ajax请求
删除评论
修改routers/posts.js
继续在post.js 后面增加
|
|
现在试试删除评论的功能吧
删除文章
只有自己发表的文字删除的文字才会显示出来,才能被删除,
修改routers/posts.js
继续在post.js 后面增加
|
|
现在试试删除文章的功能吧
编辑文章
修改routers/posts.js
继续在post.js 后面增加
|
|
修改views/edit.js
|
|
现在试试编辑文字然后修改提交吧
结语
至此一个简单的blog就已经制作好了,其他扩展功能相信你已经会了吧!如果出现问题,还望积极提问哈,我会尽快处理的
所有的代码都在 https://github.com/wclimb/Koa2-blog 里面,如果觉得不错就star一下吧。有问题可以提问哟
下一篇可能是 Node + express + mongoose 或 zepto源码系列
感谢您的阅读^_^