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