koa学习(持续更新中...)

804 阅读4分钟

1. nodejs理解

  1. nodejs的出现并不是为了开发服务端API,其主要目的是让JS脱离浏览器后仍可以运行,他由很多应用方向,正因为不是为了开发服务端API,所以其提供的API比较基础,直接用于开发服务端API时不好用,所以产生了很多基于nodejs的专门用于开发服务端API的框架。
  2. nodejs应用:

    1. NodeJS stream(前端工程化)
    2. 编写服务端API
    3. 作为中间层

2. koa特点

简单地说可以概括为两点:精简、洋葱模型。

3. koa最简单的使用

// (1)导入koa
const Koa  = require('koa')

// (2)koa实例化,即使用koa,这个app实例也称作应用程序对象,其最大的特点是在它上面包含了很多中间件
const app = new Koa()

// (3)可以注册多个中间件,ctx指的是上下文,next指的是下一个中间件
app.use((ctx, next) => {
 next()
})

// (4)启用koa
app.listen(3000)

备注:

  • 中间件其实就是函数,函数注册后即变为中间件,app.use()即为注册中间件。
  • 第一个中间件自动执行,其他中间件需要开发者自己调用。

4. 对中间件函数next()的理解

  • next()返回值一定是promise,要想得到里面的值,可以采用回调函数或者使用 async、await。
  • 中间件可以有返回值。
  • 注册中间件时,经常需要加async的原因:内部使用了await

5. 对中间件上下文ctx的理解

  • ctx就是个保存状态的对象,里面有一些原生node相关的对象。
  • 需要注意的地方是,在整个请求的生命周期中,该对象会一直存在。

6. 对async await的理解

  • async:函数加上async后返回值会被包装成promise。
  • await:(1)可以当成是求值关键字,await会计算next()返回的promise,并将其转化为值。(2)阻塞当前线程。

7. 洋葱模型

先说结论:最好所有中间件都加上async、await,如果不加,所有中间件的执行不一定保证都是按洋葱模型执行。

const Koa  = require('koa')

const app = new Koa()

app.use((ctx, next) => {  
    console.log(1) 
    next()  
    console.log(2)
})

app.use(async(ctx, next) => {  
    console.log(3)  
    const axios = require('axios')  
     //(a)这里的await会造成代码阻塞  
    const res = await axios.get('http://www.baidu.com')
    next()  
    console.log(4)
})

app.listen(3000)

上述代码执行结果是:1 3 2 4,第一个中间件并没有按洋葱模型执行,原因是(a)处的代码会阻塞当前线程,因此,打印3之后会先打印2,最后(a)处代码执行完毕后再打印了4。

如果将第一个中间件加上async await后,两个中间件都会严格按照洋葱模型执行,代码执行结果变为:1 3 4 2。

8. 路由的自动加载

1. koa-router经典入门代码示例

const Koa  = require('koa');

// (1)导入

const Router = require('koa-router');
const app = new Koa();

// (2)实例化,一个router实例对象可以编写多个路由函数

const router = new Router();

//(3)使用,第一个参数是路径,第二参数是个中间件,该中间件koa-router自己注册,不用显示注册

router.get('/', (ctx, next) => {})

//(4)将koa-router注册到koa上

app.use(router.routes())app.listen(3000);

  • 实际返回到客户端的应该是json数据,koa内部已经做了处理,只需要返回一个对象则自动变为json格式。
  • 理论上所有接口API都可以写在上面的一个文件中,只是这样不利于代码维护,因此需要进行主题划分,将相关或相似的API接口放在同一个文件中进行编写,此时项目架构变为如下格式:

2. 多router拆分路由

// app.js

const Koa  = require('koa');

// (1)导入
const book = require('./api/v1/book');
const classic = require('./api/v1/classic');
const app = new Koa();

//(2)注册
app.use(book.routes());
app.use(classic.routes());
app.listen(3000);

// book.js

const Router = require('koa-router');
const router = new Router();

router.get('/v1/book/latest', (ctx, next) => {  
    ctx.body = {    key: 'book'  }
})

module.exports = router

// classic.js

const Router = require('koa-router');
const router = new Router();

router.get('/v1/classic/latest', (ctx, next) => {
  ctx.body = {    key: 'classic'  }
})

module.exports = router

模块导入原则:上层模块可以导入下层模块,下层模块尽量避免导入上层模块。

前端传API版本号方式:

  1. 路径:v1/list
  2. 查询参数:/list?version=v1
  3. header中

app.js中路由模块的引入与注册过于繁琐,可以使用require-directory实现自动加载文件与自动注册。

3. require-directory自动引入文件与自动注册中间件

(1)安装插件

npm install require-directory --save

(2)使用

作用:

  • 自动加载某一目录下的所有文件
  • 自动注册

// app.js

const Koa  = require('koa');const Router = require('koa-router');

// (1)导入

const requireDirectory = require('require-directory');

const app = new Koa();

//(2)自动引入某一目录下的所有模块

// module为固定字

// './api'为路径,一般为文件夹,支持嵌套路径

// 第三个参数可以理解为:每加载一个模块就执行该函数

requireDirectory(module, './api', {  visit: whenLoadModule})

//(3)自动注册
function whenLoadModule(obj) { 
 if(obj instanceof Router) {    
    app.use(obj.routes())  
}}

app.listen(3000);



以上内容来自慕课实战coding.imooc.com/class/342.h…