当前位置:首页 > 文章列表 > 数据库 > MySQL > Node+Koa2+Mysql 搭建简易博客

Node+Koa2+Mysql 搭建简易博客

来源:SegmentFault 2023-02-16 15:27:47 0浏览 收藏

亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《Node+Koa2+Mysql 搭建简易博客》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下MySQL、Node.js、javascript、koa.js,希望所有认真读完的童鞋们,都有实质性的提高。

Koa2-blog

2018-1-5 更新教程(新增上传头像、新增分页、样式改版、发布文章和评论支持markdown语法)
现在GitHub的代码结构有变现在GitHub的代码结构有变,接口名也有变动。

Node+Koa2+Mysql 搭建简易博客

预览地址

http://blog.wclimb.site

写在前面

本篇教程一方面是为了自己在学习的过程加深记忆,也是总结的过程。另一方面给对这方面不太了解的同学以借鉴经验。如发现问题还望指正,
如果你觉得这篇文章帮助到了你,那就赏脸给个star吧,https://github.com/wclimb/Koa...
下一篇可能是 Node + express + mongoose 或 zepto源码系列
感谢您的阅读^_^
ps:关于markdown代码缩进问题,看起来不太舒服,但复制到编辑器是正常的哟!

演示效果

img

开发环境

  • 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和jq

    
    
        koa2-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

        img

        发表

        现在看看能不能发表吧

        即使我们发表了文章,但是当前我们的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的知识点!

版本声明
本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
【MySQL】Mysql误操作后使用binlog2sql快速回滚【MySQL】Mysql误操作后使用binlog2sql快速回滚
上一篇
【MySQL】Mysql误操作后使用binlog2sql快速回滚
【踩坑记录】记一次MySQL主从复制延迟的坑
下一篇
【踩坑记录】记一次MySQL主从复制延迟的坑
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 笔灵AI生成答辩PPT:高效制作学术与职场PPT的利器
    笔灵AI生成答辩PPT
    探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
    14次使用
  • 知网AIGC检测服务系统:精准识别学术文本中的AI生成内容
    知网AIGC检测服务系统
    知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
    22次使用
  • AIGC检测服务:AIbiye助力确保论文原创性
    AIGC检测-Aibiye
    AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
    30次使用
  • 易笔AI论文平台:快速生成高质量学术论文的利器
    易笔AI论文
    易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
    39次使用
  • 笔启AI论文写作平台:多类型论文生成与多语言支持
    笔启AI论文写作平台
    笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
    35次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码