koa是基于Node.js的一个新的web框架,它的特色是轻量、健壮、富有表现力。它是Express的下一代基于Node.js的web 框架,koa2彻底使用Promise配合async来实现异步。html
Koa将Node.js的Request请求和Response响应对象封装到Context对象中,因此也能够把Context对象成为一次对话的上下文,经过加工Context对象,就能够控制返回给用户的内容。像Express中的Req和Res都封装到了Context中。Context中还内置了一些经常使用的属性:前端
Koa的应用程序其实就是一个包含一组中间件函数的对象,经过app.use函数来加载中间件,这个函数有两个参数,context指的是上下文环境对象,封装了一些属性;next用于把中间件的执行权交给下游的中间件,在当前中间件中位于next()以后的代码会暂停执行,直到最后一个中间件执行完毕,再自下而上依次执行每一个中间件中next值周的代码,相似于栈的先进后出。这种模型被称做“洋葱圈模型”。node
洋葱圈的最外层是最上层的中间件,由上自下执行每一个中间件中next()函数以前的代码,以后由下自上执行每一个中间件中next()函数以后的代码。Koa的大部分功能都是经过中间件实现的。 web
const koa = require('koa')
const app = new koa()
app.use(async function (ctx, next) { //中间件1,位于最上层
console.log('one start') //(1)
ctx.body = 'Hello Koa' //(2)
await next()
ctx.body = ctx.body + '!!!'//(9)
console.log('one end') //(10)
})
app.use(async function (ctx, next) { //中间件2,位于中间
console.log('two start') //(3)
ctx.type = 'text/html;charset=utf-8' //(4)
await next()
console.log('two end') //(8)
})
app.use(async function (ctx, next) { //中间件3,位于最下层
console.log('three start') //(5)
ctx.body = ctx.body + ', I am zhunny' //(6)
await next()
console.log('three end') //(7)
})
app.listen(3000, () => {
console.log('server is running at http://localhost:3000')
})
复制代码
浏览器中显示以下:编程
某个中间件出错,能够在它上一级的错误处理中间件中捕获,再一层层向上捕获,从全局app应用层最后到Node层。Koa的错误处理是向上抛的。后端
const koa = require('koa')
const app = new koa()
//响应输出中间件
app.use(async function (ctx, next) {
await next()
//获取响应头,印证执行顺序
const rt = ctx.response.get('X-Response-Time')
console.log(`输出计时:${ctx.method} ${ctx.url} - ${rt}`)
})
app.use(async function (ctx, next) {
const start = Date.now()
console.log('开始计时')
await next()
const ms = Date.now() - start
ctx.set('X-Response-Time', `${ms}ms`)
console.log('计时结束')
})
//中间件错误捕获
app.use(async (ctx, next) => {
try {
await next()
} catch (error) {
ctx.status = error.statusCode || error.status || 500
ctx.body = error.message
//触发应用层级的错误事件
ctx.app.emit('error', error, ctx)
//中间件出错,能够上抛到中间件错误捕获->全局->Node
console.log('中间件捕捉', error.message)
}
})
app.use(async function (ctx, next) {
console.log('响应用户请求')
//这里设置一个未定义的函数
sleep(300)
ctx.status = 200
ctx.type = 'html'
ctx.body = '<h1>Hello Koa</h1>'
})
//全局的错误捕获
app.on('error', err => {
console.error('app全局错误:', err.message)
//继续上抛到Node,此时会停止服务
throw err
})
app.listen(3000)
复制代码
node控制台打印结果以下:浏览器
路由具备引导、匹配之意。路由匹配是根据URL的变动从新渲染页面布局和内容的过程。tomcat
早期,先后端没有分离时,由后端来实现路由。用户将每一个页面的静态资源所有都放到后端服务器上,当用户进行页面切换时,由浏览器向服务器发送不一样的URL请求,经服务器解析后向浏览器返回对应页面的静态资源和数据,再由浏览器渲染成新的页面。后端路由的弊端是每次切换页面都会刷新页面,给用户形成一种卡顿的感受,用户体验不好。先后端不分离,路由是后端开发人员来作的,整个业务偏重后端,后端开发任务繁重,且先后端不解耦,使得服务器压力大,开发效率也低。前端框架
先后端分离时代的到来,使得先后端能够并行开发,开发效率,项目性能大大提高。前端服务器负责控制页面引用&跳转&路由,前端页面异步调用后端的接口,后端/应用服务器使用tomcat(把tomcat想象成一个数据提供者),加快总体响应速度。前端路由再也不是每次都刷新页面,而是在须要的时候才加载相应页面的内容。不一样路由切换对应的仅仅是一个文档树的切换,页面所需的数据才会向后端服务器发起请求。前端路由的主要场景是SPA单页面应用,Vue、React等流行的前端框架都提供了路由切换。服务器
若是不采用路由组件应该如何根据不一样的url和方法作不一样的响应?以下自定义一个404页面:
const koa = require('koa')
const app = new koa()
app.use(async (ctx, next) => {
if (ctx.url === '/' && ctx.method === 'GET') {
ctx.body = 'Page Not Found'
ctx.status = 404
} else {
ctx.body = 'Defalut Page'
ctx.status = 200
}
await next()
})
复制代码
上述这种方式将路由处理和输出响应都放在了一个中间件函数中,而实际的项目中会存在不少的路由,若是按照这样的方式处理,会严重影响到代码的可读性和可维护性。咱们一般使用路由组件来定义路由。
koa-router具备丰富的API,能够实现命名路由、命名参数、多路由中间件、嵌套路由等多种功能。上述的代码能够用koa-router来改写:
const koa = require('koa')
const app = new koa()
const Router = require('koa-router')
const router = new Router()
router.get('/', (ctx, next) => {
ctx.body = 'Page Not Found!!!'
ctx.status = 404
})
app.use(router.routes())
app.listen(3000)
复制代码
在实际项目中,根据不一样的职能将路由分为不一样的模块,在入口文件中统一调用这些路由模块。例如如今有一个users模块,负责用户信息的增删改查,一个index模块,负责默认页面路由。
const Router = require('koa-router')
const router = new Router()
router.get('/', ctx => {
ctx.body = 'index'
})
module.exports = router
复制代码
users.js的内容以下:
const Router = require('koa-router')
const router = new Router({ prefix: '/users' })
router.get('/', ctx => {
ctx.body = 'user'
})
module.exports = router
复制代码
入口文件中使用这些路由中间件:
const index = require('./routes/index')
const users = require('./routes/users')
app.use(index.routes())
app.use(users.routes())
复制代码
浏览器中显示以下: