使用 Koa.js 搭建多人博客

简介

在本文中,我们将介绍如何使用 Koa.js 搭建一个多人博客。Koa.js 是一个基于 Node.js 的 Web 应用框架,具有轻量、灵活、易扩展等特点,用于快速构建 Web 应用程序,特别是对于 API 应用程序。本文将涉及到 Koa.js 的基本概念和用法、使用 MongoDB 存储数据、使用模板引擎渲染页面和使用第三方模块等。

前置知识

  • 了解 JavaScript 和 Node.js
  • 了解基本的 HTTP 协议和 Web 开发相关的知识

环境准备

本项目使用以下环境和工具:

  • Node.js
  • MongoDB

项目初始化

首先,我们需要创建一个空白的文件夹用作项目目录,然后通过以下命令进入该目录:

接下来,我们使用 npm 初始化该项目:

该命令会创建一个 package.json 文件,其中包含该项目的基本信息和依赖项。

接着,我们需要安装必要的依赖项。在本项目中,我们将使用以下模块:

  • koa
  • koa-router
  • koa-bodyparser
  • mongoose
  • ejs
  • marked

使用以下命令安装这些依赖项:

构建基本框架

创建一个 main.js 文件,该文件将作为主要的服务器逻辑。在该文件中,我们需要完成以下操作:

  1. 导入必要的模块

    const Koa = require('koa')
    const Router = require('koa-router')
    const bodyParser = require('koa-bodyparser')
    const mongoose = require('mongoose')
    const ejs = require('ejs')
    const marked = require('marked')
  2. 初始化 Koa 和路由

    const app = new Koa()
    const router = new Router()
  3. 连接 MongoDB 数据库

    mongoose.connect('mongodb://localhost:27017/blog', {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      useFindAndModify: false
    }).then(() => {
      console.log('Connected to database')
    }).catch((err) => {
      console.log(err)
    })
  4. 创建一个模型来表示文章

    const PostSchema = new mongoose.Schema({
      title: String,
      content: String,
      created: {
        type: Date,
        default: Date.now
      }
    })
    
    const Post = mongoose.model('Post', PostSchema)
  5. 创建一个首页路由

    router.get('/', async (ctx) => {
      const posts = await Post.find().sort('-created')
      const html = ejs.render(`
      <% for(let i=0; i<posts.length; i++) { %>
      <div>
        <h2><%= posts[i].title %></h2>
        <p><%= marked(posts[i].content) %></p>
      </div>
      <% } %>`, {posts: posts})
      ctx.body = html
    })
  6. 创建一个创建文章的路由

    router.get('/post/new', async (ctx) => {
      const html = ejs.render(`
      <form method="post" action="/post">
        <input type="text" name="title" placeholder="Title" required>
        <textarea name="content" placeholder="Content" required></textarea>
        <input type="submit" value="Publish">
      </form>
      `, {})
      ctx.body = html
    })
    
    router.post('/post', async (ctx) => {
      const post = new Post(ctx.request.body)
      try {
        await post.save()
        ctx.redirect('/')
      } catch (err) {
        console.log(err)
      }
    })
  7. 启动服务器

    app.use(bodyParser())
    app.use(router.routes())
    app.use(router.allowedMethods())
    
    app.listen(3000, () => {
      console.log('Server started')
    })

现在,运行以下命令启动服务器:

在浏览器中输入网址 http://localhost:3000 ,即可访问该博客系统的首页。

高级功能

以上仅为一个最基本的博客系统,它缺乏用户认证、分页、评论等其他功能。这里简单介绍一些高级功能的实现。

用户认证

在实现用户认证前,请确保已经安装了以下依赖:

  • passport
  • passport-local
  • password-hash-and-salt

首先,我们需要定义一个 User 模型来表示用户数据:

const UserSchema = new mongoose.Schema({
  username: String,
  password: String
})

UserSchema.plugin(passportLocalMongoose)

const User = mongoose.model('User', UserSchema)

然后,我们需要启用 passport:

const passport = require('koa-passport')
const LocalStrategy = require('passport-local').Strategy
const salt = require('password-hash-and-salt').salt

passport.serializeUser((user, done) => {
  done(null, user.id)
})

passport.deserializeUser((id, done) => {
  User.findById(id, (err, user) => {
    done(err, user)
  })
})

passport.use(new LocalStrategy({
  usernameField: 'username',
  passwordField: 'password'
}, (username, password, done) => {
  User.findOne({username: username}, (err, user) => {
    if (err) {
      return done(err)
    }
    if (!user) {
      return done(null, false, {message: 'Incorrect username'})
    }
    salt(password).hash(user.password.hash, (err, hash) => {
      if (err) {
        return done(err)
      }
      if (hash !== user.password.hash) {
        return done(null, false, {message: 'Incorrect password'})
      }
      return done(null, user)
    })
  })
}))

定义好 User 模型和 passport 后,我们需要在路由中加入认证逻辑:

router.post('/login',
  passport.authenticate('local', {
    successRedirect: '/',
    failureRedirect: '/login'
  })
)

router.post('/register', async (ctx) => {
  const user = new User({
    username: ctx.request.body.username,
    password: ctx.request.body.password
  })
  await user.setPassword(ctx.request.body.password)
  try {
    await user.save()
    ctx.redirect('/')
  } catch (err) {
    console.log(err)
  }
})

现在,我们可以在登录和注册页面加入表单,然后在需要认证的路由处理函数中加上 passport.authenticate 认证逻辑即可。

分页

在这里,我们将使用 koa-easy-paginate 模块来实现分页功能,它可以方便地将数据库查询结果分页。

首先,我们需要安装 koa-easy-paginate 模块:

我们需要在路由中加入分页逻辑:

router.get('/page/:page', async (ctx) => {
  const [pageQuery, itemCount] = await ctx.pager.getPageQuery(Post.find().sort('-created'))
  const posts = await pageQuery.exec()
  const html = ejs.render(`
  <% for(let i=0; i<posts.length; i++) { %>
  <div>
    <h2><%= posts[i].title %></h2>
    <p><%= marked(posts[i].content) %></p>
  </div>
  <% } %>
  
  <div class="pagination">
  <% for(let i=1; i<=pages; i++) { %>
    <% if (i == currentPage) { %>
      <span><%= i %></span>
    <% } else { %>
      <a href="/page/<%= i %>"><%= i %></a>
    <% } %>
  <% } %>
  </div>
  `, {
    posts: posts,
    pages: ctx.pager.totalPages(),
    currentPage: ctx.pager.currentPage()
  })
  ctx.body = html
})

现在,我们可以在首页的底部加入分页控件了。

评论

在这里,我们将使用 mongoose-comment-system 模块来实现评论功能,它可以轻松地将评论功能添加到任何 Mongoose 模型中。

首先,我们需要安装 mongoose-comment-system 模块:

接着,我们需要对 Post 模型进行扩展:

const commentSystem = require('mongoose-comment-system')

const PostSchema = new mongoose.Schema({
  title: String,
  content: String,
  created: {
    type: Date,
    default: Date.now
  }
})

PostSchema.plugin(commentSystem)

const Post = mongoose.model('Post', PostSchema)

最后,我们需要在路由中加入评论逻辑:

router.post('/post/:id/comments', async (ctx) => {
  const post = await Post.findById(ctx.params.id)
  const comment = await post.addComment({
    author: ctx.request.body.author,
    body: ctx.request.body.body
  })
  ctx.redirect(`/post/${ctx.params.id}`)
})

router.get('/post/:id', async (ctx) => {
  const post = await Post.findById(ctx.params.id)
  const comments = await post.getCommentTree()
  const html = ejs.render(`
  <h2>${post.title}</h2>
  <div>${marked(post.content)}</div>
  <br>
  <% for(let i=0; i<comments.length; i++) { %>
  <div>
    <p><%= comments[i].author %>:</p>
    <p><%= marked(comments[i].body) %></p>
    <a href="#" class="reply">回复</a>
    <form method="post" action="/post/<%= post.id %>/comments" class="reply-form" hidden>
      <input type="text" name="author" placeholder="Author" required>
      <textarea name="body" placeholder="Comment" required></textarea>
      <input type="submit" value="Reply">
    </form>
    <hr>
  </div>
  <% } %>
  
  <form method="post" action="/post/<%= post.id %>/comments">
    <input type="text" name="author" placeholder="Author" required>
    <textarea name="body" placeholder="Comment" required></textarea>
    <input type="submit" value="Comment">
  </form>
  `, {
    post: post,
    comments: comments
  })
  ctx.body = html
})

现在,我们可以在文章页面下方加入评论区了。

总结

在本文中,我们使用 Koa.js 搭建了一个多人博客系统,并实现了用户认证、分页和评论等功能。通过对 Koa.js 的学习和实践,我们可以对 Node.js Web 开发有更深入的理解,同时掌握了实际开发中的一些技巧和方法。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65912dceeb4cecbf2d6674cb


纠错
反馈