推荐

  有时间可以阅读以下文章:

# 一、Koa 基础

# 1、基本概述

  Koa 同样是由TJ Holowaychuk (opens new window) 开发的,也是现在他在维护的框架,express 已经交给团队在维护了。

  Koa 的产生是为了让 express 变得轻量级(核心代码只有 1600+行),并且 Koa 中不再提供内置中间件,一切中间件按需安装使用即可。

  Koa 的使用和 express 基本一致,不同的是:Koa 放弃了 express 中采用回调的方式触发 中间件,转而使用 asyn/await 的异步方式触发 中间件

express 采用 callback 来处理异步,Koa1 采用 generator,Koa2 采用 asyn/await

# 2、常规使用

安装:

npm install koa

使用:

与 express 框架对比
const express = require('express')

const app = express()

// 逻辑处理 —— " 中间件💖 "
// 例如:
app.post('/login', (req, res, next) => {
  //

  res.send()
})

app.listen(6061)
const Koa = require('koa')

const app = new Koa() // 类

// 逻辑处理 —— " 中间件💖 "
// 例如:
app.use(async (ctx, next) => {
  //

  ctx.res.end()
})

app.listen(6061)

# 3、ctx 上下文 🎈

  context 上下文这个概念,其实在 pinia(actions 的 context 参数)、react(Context 共享机制) 也有相关的东西。

Koa 中的 context 包含了 request 对象 和 response 对象

app.context.db = db()

app.use(async (ctx, next) => {
  //
  console.log(ctx.request) // koa封装的请求对象,可以简写为:ctx
  console.log(ctx.req) // 原生node的请求对象

  console.log(ctx.response) // koa封装的请求对象,可以简写为:ctx
  console.log(ctx.res) // 原生node的请求对象

  //
  console.log(ctx.db) // 自定义的ctx属性🚩
})
请求响应图示

# 4、错误处理

利用底层的 event 模块(EventEmitter )进行事件监听

ctx.app.emit('error', -1002, ctx)
const Koa = require('koa')

const app = new Koa()

app.on('error', (err, ctx) => {
  const errCode = typeof err === 'number' ? err : 0
  let message = '未知的错误信息'

  switch (errCode) {
    case -1001:
      message = '没有输入用户名和密码'
      break
    case -1002:
      message = '输入用户名和密码错误'
      break
    default:
      break
  }

  ctx.body = {
    code: errCode,
    msg: message
  }
})

app.listen(6061)

# 二、开发应用

TIP

  更多的 kao 官方维护的开源库,可以查看 github 的 koajs (opens new window)

  本部分的内容大部分是为了匹配 express 中的内置路由 和 官方第三方路由

# 1、koa 路由

  在 koa 中,并没有集成 express 中的 app.METHODS

  因此,如果我们要完成对app.use的进一步细分,可以采用一些出色的开源库,例如:koa-router (opens new window)@koa/router (opens new window)(两种使用一致)

使用上和 express 中的 router 中间件(express().Router())类似

代码示例
const Koa = require('koa')
const Router = require('./routes/index.js')

const app = new Koa()

// 注册koa路由
app.use(Router.routes()).use(Router.allowedMethods())

app.listen(6061)
// routes/index.js
const Router = require('@koa/router')
const router = new Router()

const postsRouter = require('./postsRouter.js')

// 注册router局部路由
router.use(postsRouter.routes(), postsRouter.allowedMethods())
// ……

module.exports = router
// routes/postsRouter.js
const Router = require('@koa/router')
const postsRouter = new Router({ prefix: '/posts' })

postsRouter
  .get('/', (ctx, next) => {})
  .get('/:id', (ctx, next) => {})
  .post('/', (ctx, next) => {})
  .path('/:id', (ctx, next) => {})
  .delete('/:id', (ctx, next) => {})

module.exports = postsRouter

# 2、请求处理 ✨

  在 express 中,我们可以使用内置中间件express.urlencoded()express.json()等来处理不同类型的请求数据,那在 koa 中我们可以使用什么库来代替呢?

提示

  Apifox 在模拟 multipart/form-data(new FormData()方式) 和 x-www-form-urlencoded(原始 form 表单方式)请求时,注意要手动添加 Content-Type,不然 Apifox 不会识别到 😭

使用 postman 则不会出现这样的问题,观察默认 Headers 数量 就知道了

代码示例
const Koa = require('koa')
const Router = require('@koa/router')
const multer = require('@koa/multer')
const { bodyParser } = require('@koa/bodyparser')

const app = new Koa()

// 处理multipart/form-data数据
const formParser = new multer()
// 处理 json/form/text/xml数据
app.use(bodyParser())

// --------------

const router = new Router({
  prefix: '/posts'
})
router
  .get('/', (ctx, next) => {
    const pathname = ctx.path
    const query = ctx.query

    console.log(pathname, query) // /posts { id: '1' }
    ctx.res.end()
  })
  .get('/:id', (ctx, next) => {
    const pathname = ctx.path
    const params = ctx.params

    console.log(pathname, params) // /posts/1 { id: '1' }
    ctx.res.end()
  })
  .post('/', (ctx, next) => {
    const pathname = ctx.path
    const body = ctx.request.body // json数据(不能简写为ctx.body) 👀

    console.log(pathname, body) // /posts { title: 'foo', body: 'bar', userId: 1 }
    ctx.res.end()
  })

  // ------

  .post('/urlencoded', (ctx, next) => {
    console.log(ctx.request.body) // urlencoded数据
  })
  .post('/formdata', formParser.any(), (ctx, next) => {
    console.log(ctx.request.body) // form-data数据
  })
app.use(router.routes()).use(router.allowedMethods())

// --------------

app.listen(6061)

# 3、文件上传

  在 express 中,我们可以使用官方第三方库 multer (opens new window) 来处理文件上传的需求,那在 koa 中我们可以使用什么库来代替呢?

提示

  后缀名(png/jpg 等)跟图片内部本身的编码格式是没有任何关系的;只要是编辑器等工具支持的图片后缀名即可。

const Koa = require('koa')
const Router = require('@koa/router')
const multer = require('@koa/multer')

const app = new Koa()

// multer 处理文件
const upload = multer({
  storage: multer.diskStorage({
    destination: function (req, file, cb) {
      cb(null, __dirname + '/tmp/uploads')
    },
    filename: function (req, file, cb) {
      cb(null, Date.now() + '_' + file.originalname) // 自定义文件名
      // cb(null, file.fieldname + '_' + Date.now() + '.' + file.mimetype.split('/')[1])
    }
  })
})

// --------------

const router = new Router()
router.post('/upload', upload.single('avatar'), (ctx, next) => {
  // ctx.request.file   // ctx.request.body
})
app.use(router.routes()).use(router.allowedMethods())

// --------------

app.listen(6061)

# 4、响应处理 ✨

const Koa = require('koa')
const Router = require('@koa/router')
const { createReadStream } = require('node:fs')

const app = new Koa()

// --------------

const router = new Router({
  prefix: '/posts'
})
router.post('/', (ctx, next) => {
  ctx.status = 403
  ctx.set('Content-Type', 'multipart/form-data;charset=utf-8')

  // ……
  // String
  ctx.body = 'lencamo'
  // Buffer
  ctx.body = Buffer.from('你好呀! node.js')
  // Stream
  const readStream = createReadStream('./icon.png')
  ctx.type = 'image/jpeg'
  ctx.body = readStream
  // Object
  ctx.body = {
    code: 1,
    message: '获取文章列表成功',
    data: []
  }
})
app.use(router.routes()).use(router.allowedMethods())

// --------------

app.listen(6061)

# 三、其他

# 1、静态资源

  在 express 中,我们可以使用内置中间件express.static可以指定哪些文件夹为静态文件夹,那在 koa 中我们可以使用什么库来代替呢?

const static = require('koa-static')

// 给静态资源访问设置一个访问前缀
app.use('/imitation-tencent', static(path.join(__dirname, 'build'))) // http://localhost:6061/imitation-tencent/build/bg.png

# 2、模板引擎 ejs

  在 express 中,我们可以使用其提供的 app.engine (opens new window) 方法指定并注册模板引擎,那在 koa 中我们可以使用什么库来代替呢?

var views = require('koa-views')

// 必须放在所有路由前面
app.use(
  views(__dirname + '/views', {
    extension: 'html',
    map: {
      html: 'ejs'
    }
  })
)
koa 中 app.context 和 ctx.state 的区别

相同点:

都是用于 存储和共享数据

不同点:

app.context 的全局共享的,而 ctx.state 可以针对特定的请求为其下游共享数据

const Koa = require('koa')
const { createReadStream } = require('node:fs')
const Router = require('koa-router')
const views = require('koa-views')

const app = new Koa()

// 运行 ejs 引擎
app.use(views(__dirname + '/views', { extension: 'html', map: { html: 'ejs' } }))

app.use(async (ctx, next) => {
  // 存储全局状态
  ctx.state.appName = '任先生的笔记'
  ctx.state.currentUser = { copyRight: 'lencamo', startTime: '2022' }

  await next()
})

// --------------

const router = new Router()
router.get('/about', async (ctx) => {
  // 静态页面
  const readStream = createReadStream(__dirname + '/views/about.html')
  ctx.type = 'html'
  ctx.body = readStream

  // 动态页面
  await ctx.render('about', { title: '自我介绍' }) // 使用/views/about.html文件(前面已经配置好了)

  // 重定向
  ctx.redirect('/hello/anime')
  ctx.redirect('http://www.example.com')
})
app.use(router.routes()).use(router.allowedMethods())

// --------------

// 启动应用
app.listen(6061)
<!DOCTYPE html>
<head>
  <title><%= appName %></title>
</head>
<body>
  <h2><%= title %></h2>
  <footer><%= currentUser.copyRight %>-<%= currentUser.startTime %></footer>
</body>

# 3、cookie 相关

  在 express 中,我们可以使用官方第三方库 cookie-parser (opens new window)来处理 HTTP 请求中的 Cookie ,那在 koa 中我们可以使用什么库来代替呢?

  • koa 内置对象 ctx.cookies
const Koa = require('koa')
const Router = require('koa-router')

const app = new Koa()

// --------------

const router = new Router()

// 前端:
// 🚩document.cookie="username=lencamo"

// 后端:
router.get('/login', async (ctx, next) => {
  // 读取Cookie
  console.log(ctx.cookies.get('password'))

  // 设置Cookie
  ctx.cookies.set('username', 'lencamo')
  ctx.res.send()
})
app.use(router.routes()).use(router.allowedMethods())

// --------------

// 启动应用
app.listen(6061)

# 4、数据库集成

更新于 : 8/7/2024, 2:16:31 PM