Node+Koa2+Mysql 搭建简易博客
来源:SegmentFault
时间:2023-02-16 15:27:47 469浏览 收藏
亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《Node+Koa2+Mysql 搭建简易博客》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下MySQL、Node.js、javascript、koa.js,希望所有认真读完的童鞋们,都有实质性的提高。
Koa2-blog
2018-1-5 更新教程(新增上传头像、新增分页、样式改版、发布文章和评论支持markdown语法)
现在GitHub的代码结构有变现在GitHub的代码结构有变,接口名也有变动。
Node+Koa2+Mysql 搭建简易博客
预览地址
写在前面
本篇教程一方面是为了自己在学习的过程加深记忆,也是总结的过程。另一方面给对这方面不太了解的同学以借鉴经验。如发现问题还望指正,
如果你觉得这篇文章帮助到了你,那就赏脸给个star吧,https://github.com/wclimb/Koa...
下一篇可能是 Node + express + mongoose 或 zepto源码系列
感谢您的阅读^_^
ps:关于markdown代码缩进问题,看起来不太舒服,但复制到编辑器是正常的哟!
演示效果
开发环境
- nodejs
const Koa = require('koa'); const path = require('path') const bodyParser = require('koa-bodyparser'); const ejs = require('ejs'); const session = require('koa-session-minimal'); const MysqlStore = require('koa-mysql-session'); const config = require('./config/default.js'); const router=require('koa-router') const views = require('koa-views') // const koaStatic = require('koa-static') const staticCache = require('koa-static-cache') const app = new Koa() // session存储配置 const sessionMysqlConfig = { user: config.database.USERNAME, password: config.database.PASSWORD, database: config.database.DATABASE, host: config.database.HOST, } // 配置session中间件 app.use(session({ key: 'USER_SID', store: new MysqlStore(sessionMysqlConfig) })) // 配置静态资源加载中间件 // app.use(koaStatic( // path.join(__dirname , './public') // )) // 缓存 app.use(staticCache(path.join(__dirname, './public'), { dynamic: true }, { maxAge: 365 * 24 * 60 * 60 })) app.use(staticCache(path.join(__dirname, './images'), { dynamic: true }, { maxAge: 365 * 24 * 60 * 60 })) // 配置服务端模板渲染引擎中间件 app.use(views(path.join(__dirname, './views'), { extension: 'ejs' })) app.use(bodyParser({ formLimit: '1mb' })) // 路由(我们先注释三个,等后面添加好了再取消注释,因为我们还没有定义路由,稍后会先实现注册) //app.use(require('./routers/signin.js').routes()) app.use(require('./routers/signup.js').routes()) //app.use(require('./routers/posts.js').routes()) //app.use(require('./routers/signout.js').routes()) app.listen(3000) console.log(`listening on port ${config.port}`)
我们使用
// 注册用户 let insertData = function( value ) { let _sql = "insert into users set name=?,pass=?,avator=?,moment=?;" return query( _sql, value ) }
我们写了一个_sql的sql语句,意思是插入到users的表中(在这之前我们已经建立了users表)然后要插入的数据分别是name、pass、avator、moment,就是用户名、密码、头像、注册时间,最后调用
var mysql = require('mysql'); var config = require('../config/default.js') var pool = mysql.createPool({ host : config.database.HOST, user : config.database.USERNAME, password : config.database.PASSWORD, database : config.database.DATABASE }); let query = function( sql, values ) { return new Promise(( resolve, reject ) => { pool.getConnection(function(err, connection) { if (err) { reject( err ) } else { connection.query(sql, values, ( err, rows) => { if ( err ) { reject( err ) } else { resolve( rows ) } connection.release() }) } }) }) } // let query = function( sql, values ) { // pool.getConnection(function(err, connection) { // // 使用连接 // connection.query( sql,values, function(err, rows) { // // 使用连接执行查询 // console.log(rows) // connection.release(); // //连接不再使用,返回到连接池 // }); // }); // } let users = `create table if not exists users( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, pass VARCHAR(100) NOT NULL, avator VARCHAR(100) NOT NULL, moment VARCHAR(100) NOT NULL, PRIMARY KEY ( id ) );` let posts = `create table if not exists posts( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, title TEXT(0) NOT NULL, content TEXT(0) NOT NULL, md TEXT(0) NOT NULL, uid VARCHAR(40) NOT NULL, moment VARCHAR(100) NOT NULL, comments VARCHAR(200) NOT NULL DEFAULT '0', pv VARCHAR(40) NOT NULL DEFAULT '0', avator VARCHAR(100) NOT NULL, PRIMARY KEY ( id ) );` let comment = `create table if not exists comment( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, content TEXT(0) NOT NULL, moment VARCHAR(40) NOT NULL, postid VARCHAR(40) NOT NULL, avator VARCHAR(100) NOT NULL, PRIMARY KEY ( id ) );` let createTable = function( sql ) { return query( sql, [] ) } // 建表 createTable(users) createTable(posts) createTable(comment) // 注册用户 let insertData = function( value ) { let _sql = "insert into users set name=?,pass=?,avator=?,moment=?;" return query( _sql, value ) } // 删除用户 let deleteUserData = function( name ) { let _sql = `delete from users where name="${name}";` return query( _sql ) } // 查找用户 let findUserData = function( name ) { let _sql = `select * from users where name="${name}";` return query( _sql ) } // 发表文章 let insertPost = function( value ) { let _sql = "insert into posts set name=?,title=?,content=?,md=?,uid=?,moment=?,avator=?;" return query( _sql, value ) } // 更新文章评论数 let updatePostComment = function( value ) { let _sql = "update posts set comments=? where id=?" return query( _sql, value ) } // 更新浏览数 let updatePostPv = function( value ) { let _sql = "update posts set pv=? where id=?" return query( _sql, value ) } // 发表评论 let insertComment = function( value ) { let _sql = "insert into comment set name=?,content=?,moment=?,postid=?,avator=?;" return query( _sql, value ) } // 通过名字查找用户 let findDataByName = function ( name ) { let _sql = `select * from users where name="${name}";` return query( _sql) } // 通过文章的名字查找用户 let findDataByUser = function ( name ) { let _sql = `select * from posts where name="${name}";` return query( _sql) } // 通过文章id查找 let findDataById = function ( id ) { let _sql = `select * from posts where id="${id}";` return query( _sql) } // 通过评论id查找 let findCommentById = function ( id ) { let _sql = `select * FROM comment where postid="${id}";` return query( _sql) } // 查询所有文章 let findAllPost = function () { let _sql = ` select * FROM posts;` return query( _sql) } // 查询分页文章 let findPostByPage = function (page) { let _sql = ` select * FROM posts limit ${(page-1)*10},10;` return query( _sql) } // 查询个人分页文章 let findPostByUserPage = function (name,page) { let _sql = ` select * FROM posts where name="${name}" order by id desc limit ${(page-1)*10},10 ;` return query( _sql) } // 更新修改文章 let updatePost = function(values){ let _sql = `update posts set title=?,content=?,md=? where id=?` return query(_sql,values) } // 删除文章 let deletePost = function(id){ let _sql = `delete from posts where id = ${id}` return query(_sql) } // 删除评论 let deleteComment = function(id){ let _sql = `delete from comment where id=${id}` return query(_sql) } // 删除所有评论 let deleteAllPostComment = function(id){ let _sql = `delete from comment where postid=${id}` return query(_sql) } // 查找评论数 let findCommentLength = function(id){ let _sql = `select content from comment where postid in (select id from posts where id=${id})` return query(_sql) } // 滚动无限加载数据 let findPageById = function(page){ let _sql = `select * from posts limit ${(page-1)*5},5;` return query(_sql) } // 评论分页 let findCommentByPage = function(page,postId){ let _sql = `select * from comment where postid=${postId} order by id desc limit ${(page-1)*10},10;` return query(_sql) } module.exports = { query, createTable, insertData, deleteUserData, findUserData, findDataByName, insertPost, findAllPost, findPostByPage, findPostByUserPage, findDataByUser, findDataById, insertComment, findCommentById, updatePost, deletePost, deleteComment, findCommentLength, updatePostComment, deleteAllPostComment, updatePostPv, findPageById, findCommentByPage }
下面是我们建的表
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
const router = require('koa-router')(); const userModel = require('../lib/mysql.js'); const md5 = require('md5') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin const moment = require('moment'); const fs = require('fs') // 注册页面 router.get('/signup', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signup', { session: ctx.session, }) }) module.exports = router
使用get方式得到'/signup'页面,然后渲染signup模板,这里我们还没有在写signup.ejs
views/signup.ejs
注册 我们先安装supervisor
body, header, ul, li, p, div, html, span, h3, a, blockquote { margin: 0; padding: 0; color: #333; } body { margin-bottom: 20px; } ul,li{ list-style-type: none; } a { text-decoration: none; } header { width: 60%; margin: 20px auto; } header:after{ content: ''; clear: both; display: table; } header .user_right{ float: right } header .user_right .active{ color: #5FB878; background: #fff; border: 1px solid #5FB878; box-shadow: 0 5px 5px #ccc; } header .user_name { float: left } .user_name { font-size: 20px; } .has_user a, .has_user span, .none_user a { padding: 5px 15px; background: #5FB878; border-radius: 15px; color: #fff; cursor: pointer; border: 1px solid #fff; transition: all 0.3s; } .has_user a:hover,.has_user span:hover{ color: #5FB878; background: #fff; border: 1px solid #5FB878; box-shadow: 0 5px 5px #ccc; } .posts{ border-radius: 4px; border: 1px solid #ddd; } .posts > li{ padding: 10px; position: relative; padding-bottom: 40px; } .posts .comment_pv{ position: absolute; bottom: 5px; right: 10px; } .posts .author{ position: absolute; left: 10px; bottom: 5px; } .posts .author span{ margin-right: 5px; } .posts > li:hover { background: #f2f2f2; } .posts > li:hover pre{ border: 1px solid #666; } .posts > li:hover .content{ border-top: 1px solid #fff; border-bottom: 1px solid #fff; } .posts > li + li{ border-top: 1px solid #ddd; } .posts li .title span{ position: absolute; left: 10px; top: 10px; color: #5FB878; font-size: 14px; } .posts li .title{ margin-left: 40px; font-size: 20px; color: #222; } .posts .userAvator{ position: absolute; left: 3px; top: 3px; width: 40px; height: 40px; border-radius: 5px; } .posts .content{ border-top: 1px solid #f2f2f2; border-bottom: 1px solid #f2f2f2; margin: 10px 0 0 0 ; padding: 10px 0; margin-left: 40px; } .userMsg img{ width: 40px; height: 40px; border-radius: 5px; margin-right: 10px; vertical-align: middle; display: inline-block; } .userMsg span{ font-size: 18px; color:#333; position: relative; top: 2px; } .posts li img{ max-width: 100%; } .spost .comment_pv{ position: absolute; top: 10px; } .spost .edit { position: absolute; right: 20px; bottom: 5px; } .spost .edit p { display: inline-block; margin-left: 10px; } .comment_wrap { width: 60%; margin: 20px auto; } .submit { display: block; width: 100px; height: 40px; line-height: 40px; text-align: center; border-radius: 4px; background: #5FB878; cursor: pointer; color: #fff; float: left; margin-top: 20px ; border:1px solid #fff; } .submit:hover{ background: #fff; color: #5FB878; border:1px solid #5FB878; } .comment_list{ border: 1px solid #ddd; border-radius: 4px; } .cmt_lists:hover{ background: #f2f2f2; } .cmt_lists + .cmt_lists{ border-top: 1px solid #ddd; } .cmt_content { padding: 10px; position: relative; border-radius: 4px; word-break: break-all; } .cmt_detail{ margin-left: 48px; } .cmt_content img{ max-width: 100%; } /* .cmt_content:after { content: '#content'; position: absolute; top: 5px; right: 5px; color: #aaa; font-size: 13px; } */ .cmt_name { position: absolute; right: 8px; bottom: 5px; color: #333; } .cmt_name a { margin-left: 5px; color: #1E9FFF; } .cmt_time{ position: absolute; font-size: 12px; right: 5px; top: 5px; color: #aaa } .form { margin: 0 auto; width: 50%; margin-top: 20px; } textarea { width: 100%; height: 150px; padding:10px 0 0 10px; font-size: 20px; border-radius: 4px; border: 1px solid #d7dde4; -webkit-appearance: none; resize: none; } textarea#spContent{ width: 98%; } .tips { margin: 20px 0; color: #ec5051; text-align: center; } .container { width: 60%; margin: 0 auto; } .form img.preview { width:100px; height:100px; border-radius: 50%; display: none; margin-top:10px; } input { display: block; width: 100%; height: 35px; font-size: 18px; padding: 6px 7px; border-radius: 4px; border: 1px solid #d7dde4; -webkit-appearance: none; } input:focus,textarea:focus{ outline: 0; box-shadow: 0 0 0 2px rgba(51,153,255,.2); border-color: #5cadff; } input:hover,input:active,textarea:hover,textarea:active{ border-color: #5cadff; } .create label { display: block; margin: 10px 0; } .comment_wrap form { width: 100%; margin-bottom: 85px; } .delete_comment, .delete_post { cursor: pointer; } .delete_comment:hover, .delete_post:hover, a:hover { color: #ec5051; } .disabled{ user-select: none; cursor: not-allowed !important; } .error{ color: #ec5051; } .success{ color: #1E9FFF; } .container{ width: 60%; margin:0 auto; } .message{ position: fixed; top: -100%; left: 50%; transform: translateX(-50%); padding: 10px 20px; background: rgba(0, 0, 0, 0.7); color: #fff; border-bottom-left-radius: 15px; border-bottom-right-radius: 15px; z-index: 99999; } .markdown pre{ display: block; overflow-x: auto; padding: 0.5em; background: #F0F0F0; border-radius: 3px; border: 1px solid #fff; } .markdown blockquote{ padding: 0 1em; color: #6a737d; border-left: 0.25em solid #dfe2e5; margin: 10px 0; } .markdown ul li{ list-style: circle; margin-top: 5px; }
我们再把模板引擎的header和footer独立出来
/views/header.ejs
顺便引入index.css和jqkoa2-blog Hello, 欢迎注册登录^_^首先我们看到用到了session.user,这个值从哪来呢?请看下面的代码
// 注册页面 router.get('/signup', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signup', { session: ctx.session, }) })
我们可以看到我们向模板传了一个session值,session:ctx.session,这个值存取的就是用户的信息,包括用户名、登录之后的id等,session一般是你关闭浏览器就过期了,等于下次打开浏览器的时候就得重新登录了,用if判断他存不存在,就可以知道用户是否需要登录,如果不存在用户,则只显示
修改views/signup.ejs
先看我们请求的url地址,是'/signup',为什么是这个呢?我们看下面这段代码(后面有完整的)
router.post('/signup', async(ctx, next) => { //console.log(ctx.request.body) let user = { name: ctx.request.body.name, pass: ctx.request.body.password, repeatpass: ctx.request.body.repeatpass, avator: ctx.request.body.avator } .... }
我们的请求方式是post,地址是
const router = require('koa-router')(); const userModel = require('../lib/mysql.js'); const md5 = require('md5') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin const moment = require('moment'); const fs = require('fs') // 注册页面 router.get('/signup', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signup', { session: ctx.session, }) }) // post 注册 router.post('/signup', async(ctx, next) => { //console.log(ctx.request.body) let user = { name: ctx.request.body.name, pass: ctx.request.body.password, repeatpass: ctx.request.body.repeatpass, avator: ctx.request.body.avator } await userModel.findDataByName(user.name) .then(async (result) => { console.log(result) if (result.length) { try { throw Error('用户已经存在') } catch (error) { //处理err console.log(error) } // 用户存在 ctx.body = { data: 1 };; } else if (user.pass !== user.repeatpass || user.pass === '') { ctx.body = { data: 2 }; } else { // ctx.session.user=ctx.request.body.name let base64Data = user.avator.replace(/^data:image\/\w+;base64,/, ""); let dataBuffer = new Buffer(base64Data, 'base64'); let getName = Number(Math.random().toString().substr(3)).toString(36) + Date.now() await fs.writeFile('./public/images/' + getName + '.png', dataBuffer, err => { if (err) throw err; console.log('头像上传成功') }); await userModel.insertData([user.name, md5(user.pass), getName, moment().format('YYYY-MM-DD HH:mm:ss')]) .then(res=>{ console.log('注册成功',res) //注册成功 ctx.body = { data: 3 }; }) } }) }) module.exports = router
- 我们使用md5实现密码加密,长度是32位的
- 使用我们之前说的
router.get('/signup',async (ctx,next)=>{ await ctx.render('signup',{ session:ctx.session, }) })
这里就用到了ejs所需的session 我们通过渲染signup.ejs模板,将值ctx.session赋值给session,之后我们就可以在signup.ejs中使用了
ejs的常用标签为:-
const router = require('koa-router')(); const userModel = require('../lib/mysql.js') const md5 = require('md5') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin router.get('/signin', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signin', { session: ctx.session, }) }) module.exports=router
修改 /views/signin.ejs
修改 index.js 文件 把下面这段代码注释去掉,之前注释是因为我们没有写signin的路由,以免报错,后面还有文章页和登出页的路由,大家记住一下
app.use(require('./routers/signin.js').routes())
现在注册一下来看看效果吧
const router = require('koa-router')(); const userModel = require('../lib/mysql.js') const md5 = require('md5') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin router.get('/signin', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signin', { session: ctx.session, }) }) router.post('/signin', async(ctx, next) => { console.log(ctx.request.body) let name = ctx.request.body.name; let pass = ctx.request.body.password; await userModel.findDataByName(name) .then(result => { let res = result if (name === res[0]['name'] && md5(pass) === res[0]['pass']) { ctx.body = true ctx.session.user = res[0]['name'] ctx.session.id = res[0]['id'] console.log('ctx.session.id', ctx.session.id) console.log('session', ctx.session) console.log('登录成功') }else{ ctx.body = false console.log('用户名或密码错误!') } }).catch(err => { console.log(err) }) }) module.exports = router
我们进行登录操作,判断登录的用户名和密码是否有误,使用md5加密
我们可以看到登录成功返回的结果是我们增加了ajax请求,在routers/signin.js里,我们设置如果登录失败就返回false,登录成功返回true
const router = require('koa-router')(); const userModel = require('../lib/mysql.js') const moment = require('moment') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin; const md = require('markdown-it')(); // 重置到文章页 router.get('/', async(ctx, next) => { ctx.redirect('/posts') }) // 文章页 router.get('/posts', async(ctx, next) => { let res, postsLength, name = decodeURIComponent(ctx.request.querystring.split('=')[1]); if (ctx.request.querystring) { console.log('ctx.request.querystring', name) await userModel.findDataByUser(name) .then(result => { postsLength = result.length }) await userModel.findPostByUserPage(name,1) .then(result => { res = result }) await ctx.render('selfPosts', { session: ctx.session, posts: res, postsPageLength:Math.ceil(postsLength / 10), }) } else { await userModel.findPostByPage(1) .then(result => { //console.log(result) res = result }) await userModel.findAllPost() .then(result=>{ postsLength = result.length }) await ctx.render('posts', { session: ctx.session, posts: res, postsLength: postsLength, postsPageLength: Math.ceil(postsLength / 10), }) } }) // 首页分页,每次输出10条 router.post('/posts/page', async(ctx, next) => { let page = ctx.request.body.page; await userModel.findPostByPage(page) .then(result=>{ //console.log(result) ctx.body = result }).catch(()=>{ ctx.body = 'error' }) }) // 个人文章分页,每次输出10条 router.post('/posts/self/page', async(ctx, next) => { let data = ctx.request.body await userModel.findPostByUserPage(data.name,data.page) .then(result=>{ //console.log(result) ctx.body = result }).catch(()=>{ ctx.body = 'error' }) }) module.exports = router
修改 index.js
app.use(require('./routers/posts.js').routes())的注释去掉
修改 views/posts.ejs
const router = require('koa-router')(); router.get('/signout', async(ctx, next) => { ctx.session = null; console.log('登出成功') ctx.body = true }) module.exports = router
把session设置为null即可
修改 index.js
app.use(require('./routers/posts.js').routes())的注释去掉,现在把注释的路由全部取消注释就对了
然后我们看看
// 发表文章页面 router.get('/create', async(ctx, next) => { await ctx.render('create', { session: ctx.session, }) }) // post 发表文章 router.post('/create', async(ctx, next) => { let title = ctx.request.body.title, content = ctx.request.body.content, id = ctx.session.id, name = ctx.session.user, time = moment().format('YYYY-MM-DD HH:mm:ss'), avator, // 现在使用markdown不需要单独转义 newContent = content.replace(/[']/g, (target) => { return { '': '>', "'": ''' }[target] }), newTitle = title.replace(/[']/g, (target) => { return { '': '>', "'": ''' }[target] }); //console.log([name, newTitle, content, id, time]) await userModel.findUserData(ctx.session.user) .then(res => { console.log(res[0]['avator']) avator = res[0]['avator'] }) await userModel.insertPost([name, newTitle, md.render(content), content, id, time,avator]) .then(() => { ctx.body = true }).catch(() => { ctx.body = false }) })
修改 views/create.ejs
create.ejs
现在看看能不能发表吧
即使我们发表了文章,但是当前我们的posts的页面没有显示,因为还没有获取到数据
我们可以看我们之前写的代码
if (ctx.request.querystring) { ... }else { await userModel.findPostByPage(1) .then(result => { //console.log(result) res = result }) await userModel.findAllPost() .then(result=>{ postsLength = result.length }) await ctx.render('posts', { session: ctx.session, posts: res, postsLength: postsLength, postsPageLength: Math.ceil(postsLength / 10), }) }
if前面这部分我们先不用管,后面会说。只需要看else后面的代码我们通过
现在点击单篇文章试试,进入单篇文章页面,但是编辑、删除、评论都还没有做,点击无效,我们先不做,先实现每个用户自己发表的文章列表,我们之前在 get '/posts' 里面说先忽略if (ctx.request.querystring) {}里面的代码,这里是做了一个处理,假如用户点击了某个用户,该用户发表了几篇文章,我们需要只显示该用户发表的文章,那么进入的url应该是 /posts?author=xxx ,这个处理在posts.ejs 就已经加上了,就在文章的左下角,作者:xxx就是一个链接。我们通过判断用户来查找文章,继而有了
if (ctx.request.querystring) { console.log('ctx.request.querystring', name) await userModel.findDataByUser(name) .then(result => { postsLength = result.length }) await userModel.findPostByUserPage(name,1) .then(result => { res = result }) await ctx.render('selfPosts', { session: ctx.session, posts: res, postsPageLength:Math.ceil(postsLength / 10), }) }
修改 selfPost.ejs
'
)
})
}else{
alert('分页不存在')
}
}
})
})
编辑文章、删除文章、评论、删除评论
评论
修改routers/posts.js
在post.js 后面增加
// 发表评论 router.post('/:postId', async(ctx, next) => { let name = ctx.session.user, content = ctx.request.body.content, postId = ctx.params.postId, res_comments, time = moment().format('YYYY-MM-DD HH:mm:ss'), avator; await userModel.findUserData(ctx.session.user) .then(res => { console.log(res[0]['avator']) avator = res[0]['avator'] }) await userModel.insertComment([name, md.render(content),time, postId,avator]) await userModel.findDataById(postId) .then(result => { res_comments = parseInt(result[0]['comments']) res_comments += 1 }) await userModel.updatePostComment([res_comments, postId]) .then(() => { ctx.body = true }).catch(() => { ctx.body = false }) }) // 评论分页 router.post('/posts/:postId/commentPage', async function(ctx){ let postId = ctx.params.postId, page = ctx.request.body.page; await userModel.findCommentByPage(page,postId) .then(res=>{ ctx.body = res }).catch(()=>{ ctx.body = 'error' }) })
现在试试发表评论的功能吧,之所以这样简单,因为我们之前就在sPost.ejs做了好几个ajax的处理,删除文章和评论也是如此
评论我们也做了分页,所以后面会有一个评论的分页的接口,我们已经在sPost.ejs里面写好了ajax请求删除评论
修改routers/posts.js
继续在post.js 后面增加
// 删除评论 router.post('/posts/:postId/comment/:commentId/remove', async(ctx, next) => { let postId = ctx.params.postId, commentId = ctx.params.commentId, res_comments; await userModel.findDataById(postId) .then(result => { res_comments = parseInt(result[0]['comments']) //console.log('res', res_comments) res_comments -= 1 //console.log(res_comments) }) await userModel.updatePostComment([res_comments, postId]) await userModel.deleteComment(commentId) .then(() => { ctx.body = { data: 1 } }).catch(() => { ctx.body = { data: 2 } }) })
现在试试删除评论的功能吧
删除文章
只有自己发表的文字删除的文字才会显示出来,才能被删除,
修改routers/posts.js
继续在post.js 后面增加
// 删除单篇文章 router.post('/posts/:postId/remove', async(ctx, next) => { let postId = ctx.params.postId await userModel.deleteAllPostComment(postId) await userModel.deletePost(postId) .then(() => { ctx.body = { data: 1 } }).catch(() => { ctx.body = { data: 2 } }) })
现在试试删除文章的功能吧
编辑文章
修改routers/posts.js
继续在post.js 后面增加
// 编辑单篇文章页面 router.get('/posts/:postId/edit', async(ctx, next) => { let name = ctx.session.user, postId = ctx.params.postId, res; await userModel.findDataById(postId) .then(result => { res = result[0] //console.log('修改文章', res) }) await ctx.render('edit', { session: ctx.session, postsContent: res.md, postsTitle: res.title }) }) // post 编辑单篇文章 router.post('/posts/:postId/edit', async(ctx, next) => { let title = ctx.request.body.title, content = ctx.request.body.content, id = ctx.session.id, postId = ctx.params.postId, // 现在使用markdown不需要单独转义 newTitle = title.replace(/[']/g, (target) => { return { '': '>', "'": ''' }[target] }), newContent = content.replace(/[']/g, (target) => { return { '': '>', "'": ''' }[target] }); await userModel.updatePost([newTitle, md.render(content), content, postId]) .then(() => { ctx.body = true }).catch(() => { ctx.body = false }) })
修改views/edit.js
现在试试编辑文字然后修改提交吧
结语
至此一个简单的blog就已经制作好了,其他扩展功能相信你已经会了吧!如果出现问题,还望积极提问哈,我会尽快处理的
所有的代码都在 https://github.com/wclimb/Koa... 里面,如果觉得不错就star一下吧。有问题可以提问哟
下一篇可能是 Node + express + mongoose 或 zepto源码系列
感谢您的阅读^_^到这里,我们也就讲完了《Node+Koa2+Mysql 搭建简易博客》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于mysql的知识点!
-
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
445 收藏
-
366 收藏
-
411 收藏
-
270 收藏
-
107 收藏
-
495 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习