上一期連接——也就是本文的基礎,參考KOA,5步手寫一款粗糙的web框架html
本文參考倉庫:點我git
Router其實就是路徑匹配,經過匹配路徑,返回給用戶相應的網站內容。github
如下方例子爲例,主要經過提取req
中的path
信息,來匹配當前路徑,並給ctx.body
賦值,返回相應的界面。這個過程不復雜,就是一個匹配路徑的過程。可是這種會不會太臃腫了呢,並且頗有可能路徑一多,就要被if...else...
給弄暈了。web
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
,專門用來管理路徑。數組
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" })