上一期连接——也就是本文的基础,参考KOA,5步手写一款粗糙的web框架git
本文参考仓库:点我github
Router其实就是路径匹配,经过匹配路径,返回给用户相应的网站内容。web
如下方例子为例,主要经过提取req
中的path
信息,来匹配当前路径,并给ctx.body
赋值,返回相应的界面。这个过程不复杂,就是一个匹配路径的过程。可是这种会不会太臃肿了呢,并且颇有可能路径一多,就要被if...else...
给弄晕了。数组
app.use((ctx,next)=>{
//简易路由
let {path}=ctx
if(path==="/"){
ctx.body="index"
}else if(path==="/admin"){
ctx.body="admin"
}else if(path==="/user"){
ctx.body="user"
}
})
复制代码
这个时候专门处理路径的插件就出现了,写一个Router
,专门用来管理路径。bash
Router的功能一共是两个:app
若是Router要挂载到app上,那么语法是这样的app.use(router.routes())
,也就是说:框架
app
上了解了Router的大概,咱们开始一步步动手写Router吧!koa
先把Router的框架写好,一个构造器,一个get
方法用于配置路由,一个routers
变成路由匹配的中间件挂在到app上。异步
class Router{
constructor(){}
get(path,callback){}
routers(){}
}
复制代码
咱们获取路由的时候,必定会配置页面,那么这个页面的类也要加上了,每次get
的时候,就加入一个页面到数组中。async
class Page{
constructor(path,callback){
this.path=path
this.callback=callback
}
}
class Router{
constructor(){
this.pages=[]
}
get(path,callback){
this.pages.push(new Page(path,callback))
}
routers(){}
}
复制代码
由于路由是对中间件的封装,因此用法上是和app.use
相似的:
router.get(path,(ctx,next){
ctx.body='xxx'
next()
})
复制代码
是否是很眼熟?这个get中的callback
参数就是中间件。
routers
就干三件事:
array.filter
就能够作到compose(ctx,next,routers){
function dispatch(index){
if(index===routers.length) return next();
let router=routers[index]
router(ctx,()=>dispatch(index+1));
}
dispatch(0)
}
routers(){
let dispatch = (ctx,next)=>{
let path=ctx.path
let routers=this.pages.filter(p=>{console.log(p.path);return p.path===path}).map(p=>p.callback)
this.compose(ctx,next,routers)
}
return dispatch
}
复制代码
你们有没有很眼熟,和koa中的application.js的回调很像。其实就是一个回调的过程,封装以后,便于咱们使用。
咱们再写路由的时候,若是所有写全路径,感受会很啰嗦:
router.get("/admin",(ctx,next)=>{})
router.get("/admin/login",(ctx,next)=>{})
router.get("/admin/register",(ctx,next)=>{})
...
router.get("/user",(ctx,next)=>{})
router.get("/user/login",(ctx,next)=>{})
router.get("/user/register",(ctx,next)=>{})
....
复制代码
咱们给路由分组,其实思路很简单,就是给每一个小路由新建一个Router,而后大路由用use
方法,将这些路由集合到一块儿。
let admin=new Router()
admin.get("/",(ctx,next)=>{
ctx.body="admin"
next()
})
let user=new Router()
user.get("/",(ctx,next)=>{
ctx.body="user"
next()
})
//链式调用~
let router=new Router()
router.use("/admin",admin.routers())
.use("/user",user.routers())
app.use(router.routers())
复制代码
那么问题来了,use
要怎么写呢才能组合这些routers??咱们先来分析下use
的功能:
use
中有两个参数一个path
,一个router.routers()
的中间件,但是咱们须要router数组对象,因此咱们能够这么作:
routers(){
let dispatch = (ctx,next)=>{
.....
}
dispatch.router=this
return dispatch
}
复制代码
在中间件上暗搓搓地加一个router的对象,将本身一块儿传递出去,有么有很机智
有了router
的数组对象,那么use
这个方法就很好实现了,将page
循环一波,加入当前对象的pages
,就行了。这里再将本身返回,而后就能够愉快地使用链式调用了。
use(path,middleware) {
let router = this;
middleware.router.pages.forEach(p => {
router.get(path+p.path,p.callback)
});
return router
}
复制代码
你们须要注意,还记得上一期讲的async/await异步吗?
若是有任何除了路由的操做都要放在路由上方执行,由于路由只是匹配路径,返回结果,并无async/await操做。
因此必定注意:
这样是有效的·,页面返回aaa
app.use(async (ctx,next)=>{
await makeAPromise(ctx).then(()=>{next()})
})
...
app.use(router.routers())
复制代码
这样是无效的,页面不会返回aaa
...
app.use(router.routers())
app.use(async(ctx,next)=>{
await next()//等待下方完成后再继续执行
ctx.body="aaa"
})
复制代码