Koa(基础)

261 阅读6分钟

koa简介

Koa是一个新的web框架。通过利用async函数,Koa帮你丢弃回调函数,并有力地增强错误处理。

安装koa

cnpm i koa -S

1.使用koa搭建简单服务器

1.1 引入模块

1.2 实例化一个app

1.3 app.listen()监听

// 引入
const Koa = require('koa')
// 实例化app
const app = new Koa();

// 监听
app.listen(3000);

2.中间件middleware的使用

中间件就是一个异步的函数 主要对我们的请求和响应做预处理

app.use(async (ctx,next)=>{})

  • ctx:上下文(Context)
    • Koa Context 将 node 的 request 和 response 对象封装到单个对象中
  • await next() 作为请求和响应的分割线(具体代码注释里有解释)

ps:因为这个是异步操作,会等所有app.use的请求部分做完 再根据await next()继续处理相应部分 故称为请求和响应的分界线

const Koa = require('koa')
const app = new Koa();

app.use(async (ctx,next)=>{
  // 对请求做处理

  await next(); // 相当于请求和响应的分割线
  // 对响应做处理
  const rt = ctx.response.get('X-response-Time');
  console.log(`${ctx.method} ${ctx.url} - ${rt}`);
})

// x-response-time
app.use(async (ctx, next) => {
  // 获取请求时当前时间
  const start = Date.now();
  await next(); 
  // 当前时间-请求时时间
  const ms = Date.now() - start;
  ctx.set('X-response-Time',`${ms}ms`);
})

//response
app.use(async ctx => {
  ctx.status = 200;
  ctx.type = 'html';
  ctx.body = 'hello qlq';
})

app.listen(3000);

3.错误处理中间件的使用

3.1 先模拟一个错误ctx.throw(401,'出错')

ctx.throw(401,'出错')相当于

  const err = new Error('出错');
  err.status = 401;
  // expose为true的错误,适用于客户端
  err.expose = true;
  throw err;

3.2 用koa.on()监听error方法

app.on('error',err=>{
  console.log(err.message);
})

3.3 建立一个错误处理中间件来处理错误

与平常的中间件一样,但是添加了try/catch来捕捉错误 在catch暴露出错误.
catch中可以用ctx.app.emit('error',error,ctx)来触发app.on()监听error的方法

ps:错误处理中间件一定要写在所有中间件的最上面

const Koa = require('koa')
const app = new Koa();

// 错误处理中间件一定要写在所有中间件的最上面
app.use(async (ctx,next)=>{
  try {
    await next();
  } catch (error) {
    // 给用户显示状态码
    ctx.status = error.status || 500;
    console.log(ctx.status);
    // 前端发起ajax请求
    ctx.type = 'json';
    ctx.body = {ok:0,message: error.message}
    // 手动触发全局的错误函数
    ctx.app.emit('error',error,ctx)
  }
})

// 抛出异常 ctx.throw() 相当于中间件 请求的api接口
app.use(async ctx => {
  //请求来了
  ctx.throw(401, '出现错误了')
})

// koa监听error的方法
app.on('error',err=>{
  console.log(err.message);
})

app.listen(3000);

4. 使用koa-onerror中间件处理错误

4.1 下载koa-onerror 并引入 还要引入fs模块

4.2 onerror(app)来监听

4.3 在错误处理中间件的catch中用 ctx.body = fs.createReadStream('not exist')

const Koa = require('koa')
const onerror = require('koa-onerror')
const fs = require('fs')
const app = new Koa();

//onerror监听app
onerror(app);

// 错误处理的中间件函数
app.use(async ctx=>{
  try {
    await next();
  } catch (error) {
    ctx.body = fs.createReadStream('not exist')
  }
})

app.use(async ctx =>{
  ctx.throw(401,'出错')
})

app.on('error',err=>{
  console.log(err.message);
})

app.listen(3000);

5. koa-logger中间件的使用

5.1 下载并引入koa-logger

5.2 app.use(logger)就可以获取请求跟响应的时间等信息

例如:

const Koa = require('koa')
const logger = require('koa-logger')
const app = new Koa();
app.use(logger())

app.listen(3000);

6. koa-router中间件的使用

下载koa-router

6.1 创建一个router文件夹(装路由)。创建一个index.js

6.2 引入koa-router 并实例化一个router 并且抛出router

6.3 router.get/post('路径',(ctx,next)=>{})

这里跟node中 app.get/post()用法很多地方一样

6.4 在服务器文件下引入router文件并且注册路由

引入router文件

const routerIndex = require('./router')

注册路由

app.use(routerIndex.routes());
app.use(routerIndex.allowedMethods());

6.5 router.post(ctx=>{ctx.request.body})是获取不到请求体的

需要额外下载koa-bodyparser进行处理才能获取请求体的参数
并且在服务器文件下载并引入koa-bodyparser

const bodyParser = require('koa-bodyparser')
app.use(bodyParser());

这样便可以在路由文件下获取ctx.request.body信息

6.6 await ctx.render('要渲染的文件名',要渲染的数据)

路由文件index.js代码

const Router = require('koa-router')
const router = new Router();
//路由中间件 处理请求 定义api接口 

router.get('/',async (ctx,next)=>{
  //渲染一个页面 模板引擎 hbs

  //链接数据库拿到数据
  const data = {
    title: '我不想用mongdb',
    subTitle: 'hello hubs',
    htmlStr:'<h3>hello 缺钱</h3>',
    isShow: false,
    username:'张三',
    a: true,
    b: true,
    arr:['a','c','e'],
    users:[
      {
        name:'小黄',
        age: 12,
        birthday:new Date(1999,2,2)
      }, {
        name: '小红',
        age: 13,
        birthday: new Date(1993, 2, 2)
      }, {
        name: '小绿',
        age: 14,
        birthday: new Date(1992, 2, 2)
      },
    ]
  }

  await ctx.render('index',data) //views文件下的
})

router.get('/login', async (ctx, next) => {
  //重定向
  ctx.redirect('/sign')
})

router.get('/sign', async (ctx, next) => {
  ctx.body = '注册页面'
})

router.post('/',ctx=>{
  //post 请求需要额外下载koa-bodyparser进行处理才能获取请求体的参数
  console.log(ctx.request.body);
  ctx.body = { 'ok':1 }
})

module.exports = router;

服务器文件代码

const Koa = require('koa')
const app = new Koa();
//在koa处理post请求体的参数需要用koa-bodyparser
const bodyParser = require('koa-bodyparser')
//导入路由
const routerIndex = require('./router')

app.use(bodyParser());

//注册路由
app.use(routerIndex.routes());
app.use(routerIndex.allowedMethods());

app.listen(3000);

7. params、query、context命名空间、静态资源中间件

7.1 params

7.1.1 新建一个user的路由 服务器文件自行引入注册对应路由

7.1.2 router.prefix('/user') 下面用的路径名会自动添加prefix里的

7.1.3 params处理 router.get('/:id/:pid')

路径名后跟 : 表示后面的属性为params

7.1.4 可在回调函数中直接使用ctx.params获取params

7.2 query

http://localhost:3000/user?name=zhangsan&age=18 用ctx.query 便可获取{name=zhangsan,age=18}

7.3 context命名空间

context对象的命名空间(共享空间) 命名完 所有路由都能用state里的数据

ctx.state.xx={};

在服务器文件里的app.use中间件里ctx.state.navList={a:1}; 在所有router都可以通过ctx.state.navList来获取值

7.4 静态资源中间件

设置静态资源服务器,用来设置获取例如图片等资源的路径 这里设置public文件来装图片等资源

7.4.1 下载并引入koa-static

const static = require('koa-static')

7.4.2 设置静态资源服务器

app.use(static(__dirname+'/public'))
//src='/images/1.png'  public文件下的images文件下的1.png
路由user代码
const Router = require('koa-router')
const router = new Router({
  prefix: '/user'
});
// router.prefix('/user');

// http://localhost:3000/user?name=zhangsan&age=18
router.get('/',(ctx,next)=>{
  console.log(ctx.state.navList);
  console.log(ctx.query);
  ctx.body = "我是用户页面"
})

// http://localhost:3000/user/1/3
router.get('/:id/:pid',(ctx,next)=>{
  console.log(ctx.params);
  ctx.body = 'parmas 用法'
})

module.exports = router;
服务器代码
const Koa = require('koa')
const app = new Koa();

//在koa处理post请求体的参数需要用koa-bodyparser
const bodyParser = require('koa-bodyparser')

//导入路由
const routerIndex = require('./router')
const routerUser = require('./router/user')

//引入静态资源文件中间件
const static = require('koa-static')

app.use(async (ctx,next)=>{
  //context对象的命名空间(共享空间) 命名完 所有路由都能用state里的数据
  ctx.state.navList = {
    a:1
  }
  await next();
})

app.use(bodyParser());

//设置静态资源服务器
app.use(static(__dirname+'/public'))
//src='/images/1.png'

//注册路由
app.use(routerIndex.routes());
app.use(routerIndex.allowedMethods());
//注册用户路由
app.use(routerUser.routes());
app.use(routerUser.allowedMethods())

app.listen(3000);

8 koa-hbs模板引擎

渲染模板到前端页面

8.1 服务器文件下载并引入koa-hbs

const hbs = require('koa-hbs')

8.2 注册模板引擎的中间件

app.use(hbs.middleware({配置})

  • viewPath:视图的根目录
  • defaultLayout: 默认布局文件
  • partialsPath: 公用views路径
  • disableCache: 是否缓存
app.use(hbs.middleware({
  viewPath:__dirname+'/views',//视图的根目录
  defaultLayout:'layout',
  partialsPath:__dirname+'/views/partials', //公用views
  disableCache: true //开发阶段不缓存
}))

8.3 创建views文件夹 创建默认布局layout.hbs(注意是hbs文件)

layout.hbs是一个html文件的写法
用 { { } } 插主体 用{ {>公共views} } nav跟foot文件在partials文件下

主体内容部分 固定写法 固定是views下的index.hbs文件

layout.hub代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{title}}</title>
  <link rel="stylesheet" href="/styles/index.css">
  {{#block 'indexCss'}}{{/block}}
</head>
<body>
  {{!-- 导航部分 --}}
  {{>nav}}
  {{!-- 主体内容部分 固定写法 插入子模板主体的固定写法--}}
  {{{body}}} {{!-- index.hbs --}} 
  {{!-- 脚部 --}}
  {{>foot}}

  {{!-- 代码搬家是块 --}}
  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
  {{#block 'jquery'}}{{/block}}
</body>
</html>

8.4 在index.hbs 使用handlebars-helpers中间件

最好下载handlebars-helpers来辅助 因为原生koa-hbs对于插值引用的方法不多

const helpers = require('handlebars-helpers')
helpers.comparison({
  handlebars:hbs.handlebars
})

详情参考: github.com/helpers/han…

8.5 在路由中 await ctx.render('要显示的hbs',要传输的数据)

实例看index.hbs的代码 (数据都在上面的index视图里 要用ctx.render())
{{!-- 主体内容 --}}

{{#contentFor 'indexCss'}}
<style>
  h3 {
    color: red
  }
</style>
{{/contentFor}}

{{!-- 插值绑定(双大括号) --}}
<h3>{{subTitle}}</h3>

{{!-- 插html(三大括号) --}}
<div>{{{htmlStr}}}</div>

{{!-- 条件判断(#if是最开始的if) --}}
{{#if isShow}} 
<p>{{username}},欢迎您!</p>
{{else}}
<a href="http://localhost:3000/login">请登录</a>
{{/if}}

{{!-- 循环 --}}
<ul>
  {{#each users }}
    <li>{{name}}-{{age}}-{{data birthday 'YYYY/MM/DD'}}</li>
  {{/each}}
</ul>

<div>
  {{#and a b}}
  a,b都是true
  {{/and}}
</div>

<div>
  {{#contains arr 'e'}}
  e在数组中
  {{else}}
  e不在数组里
  {{/contains}}
</div>

{{!-- js --}}
{{#contentFor 'jquery'}}
<script>
  $(function(){
    console.log('content for jquery');
  })
</script>
{{/contentFor}}

8.6 代码搬家

在默认布局文件 这里是layout.hbs 用{{#block 'block名'}}{{/block}}
指代码搬到的地方 在其他视图文件中 {{#contentFor 'block名'}}要搬运的代码{{/contentFor}}

例:

{{#contentFor 'jquery'}}
<script>
  $(function(){
    console.log('content for jquery');
  })
</script>
{{/contentFor}}
{{#block 'jquery'}}{{/block}}