摘要:本打算教一步步實(shí)現(xiàn),因?yàn)橐忉尩奶嗔?,所以先?jiǎn)化成版本,從實(shí)現(xiàn)部分功能到閱讀源碼,希望能讓你好理解一些。
本打算教一步步實(shí)現(xiàn)koa-router,因?yàn)橐忉尩奶嗔?,所以先?jiǎn)化成mini版本,從實(shí)現(xiàn)部分功能到閱讀源碼,希望能讓你好理解一些。
希望你之前有讀過(guò)koa源碼,沒(méi)有的話,給你鏈接
router最重要的就是路由匹配,我們就從最核心的入手
router.get("/string",async (ctx, next) => { ctx.body = "koa2 string" }) router.get("/json",async (ctx, next) => { ctx.body = "koa2 json" })
我們希望
路徑訪問(wèn) /string 頁(yè)面顯示 "koa2 string"
路徑訪問(wèn) /json 頁(yè)面顯示 "koa2 json"
先分析1.我們需要一個(gè)數(shù)組,數(shù)組里每個(gè)都是一個(gè)對(duì)象,每個(gè)對(duì)象包含路徑,方法,函數(shù),傳參等信息
這個(gè)數(shù)組我們起個(gè)名字叫stack
const stack = []
2.對(duì)于每一個(gè)對(duì)象,我們起名叫l(wèi)ayer
我們把它定義成一個(gè)函數(shù)
function Layer() { }
我們把頁(yè)面比喻成一個(gè)箱子,箱子是對(duì)外的,箱子需要有入口,需要容納。把每一個(gè)router比作放在箱子里的物件,物件是內(nèi)部的
定義兩個(gè)js頁(yè)面,router.js做為入口,對(duì)于當(dāng)前頁(yè)面的訪問(wèn)的處理,layer.js包含開(kāi)發(fā)者已經(jīng)約定好的規(guī)則
router.js
module.exports = Router; function Router(opts) { // 容納layer層 this.stack = []; };
layer.js
module.exports = Layer; function Layer() { };
我們?cè)赗outer要放上許多方法,我們可以在Router內(nèi)部掛載方法,也可以在原型上掛載函數(shù)
但是要考慮多可能Router要被多次實(shí)例化,這樣里面都要開(kāi)辟一份新的空間,掛載在原型就是同一份空間。
最終決定掛載在原型上
方法有很多,我們先實(shí)現(xiàn)約定幾個(gè)常用的吧
const methods = [ "get", "post", "put", "head", "delete", "options", ];
methods.forEach(function(method) { Router.prototype[method] = function(path,middleware){ // 對(duì)于path,middleware,我們需要把它交給layer,拿到layer返回的結(jié)果 // 這里交給另一個(gè)函數(shù)來(lái)是實(shí)現(xiàn),我們叫它register就是暫存的意思 this.register(path, [method], middleware); // 因?yàn)間et還可以繼續(xù)get,我們返回this return this }; });實(shí)現(xiàn)layer的溝通
Router.prototype.register = function (path, methods, middleware) { let stack = this.stack; let route = new Layer(path, methods, middleware); stack.push(route); return route };
這里我們先去寫(xiě)layer
const pathToRegExp = require("path-to-regexp"); function Layer(path, methods, middleware) { // 把方法名稱放到methods數(shù)組里 this.methods = []; // stack盛放中間件函數(shù) this.stack = Array.isArray(middleware) ? middleware : [middleware]; // 路徑 this.path = path; // 對(duì)于這個(gè)路徑生成匹配規(guī)則,這里借助第三方 this.regexp = pathToRegExp(path); // methods methods.forEach(function(method) { this.methods.push(method.toUpperCase()); // 綁定layer的this,不然匿名函數(shù)的this指向window }, this); }; // 給一個(gè)原型方法match匹配返回true Layer.prototype.match = function (path) { return this.regexp.test(path); };
回到router層
定義match方法,根據(jù)Developer傳入的path, method返回 一個(gè)對(duì)象(包括是否匹配,匹配成功layer,和匹配成功的方法)
Router.prototype.match = function (path, method) { const layers = this.stack; let layer; const matched = { path: [], pathAndMethod: [], route: false }; //循環(huán)寄存好的stack層的每一個(gè)layer for (var len = layers.length, i = 0; i < len; i++) { layer = layers[i]; //layer是提前存好的路徑, path是過(guò)來(lái)的path if (layer.match(path)) { // layer放入path,為什么不把path傳入,一是path已經(jīng)沒(méi)用了,匹配了就夠了,layer含有更多信息需要用 matched.path.push(layer); //如果methods什么也沒(méi)寫(xiě),或者如果方法里含有你的過(guò)來(lái)的方法,那么把layer放入pathAndMethod if (layer.methods.length === 0 || ~layer.methods.indexOf(method)) { matched.pathAndMethod.push(layer); // 路徑匹配,并且有方法 if (layer.methods.length) matched.route = true; } } } return matched; };
給Developer一個(gè)方法
app.use(index.routes())
這里不考慮傳多個(gè)id,和多次匹配情況,拿到匹配的函數(shù)
Router.prototype.routes = function(){ var router = this; const dispatch = function dispatch(ctx, next) { const path = ctx.path const method = ctx.method const matched = router.match(path, ctx.method); if (!matched.route) return next(); const matchedLayers = matched.pathAndMethod // 先不考慮多matchedLayers多stack情況 return matchedLayers[0].stack[0](ctx, next); } return dispatch }
此時(shí)一個(gè)迷你koa-router已經(jīng)實(shí)現(xiàn)了
讀源碼 需求實(shí)現(xiàn) 實(shí)現(xiàn)匹配方法名匹配,路徑匹配,還要滿足動(dòng)態(tài)參數(shù)的傳遞
并且還要給很懶的開(kāi)發(fā)者一個(gè)router.all()
也就是說(shuō)不用區(qū)分方法了
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102805.html
摘要:?jiǎn)栴}描述在使用作為路由遇到了一個(gè)優(yōu)先級(jí)問(wèn)題如下代碼在訪問(wèn)時(shí)路由會(huì)優(yōu)先匹配到路由返回這個(gè)問(wèn)題就很尷尬了項(xiàng)目空閑下來(lái)去翻看源碼終于找到了原因問(wèn)題原因的源碼并不長(zhǎng)和兩個(gè)文件加起來(lái)共一千多行代碼建議可以結(jié)合這篇文章閱讀其中造成這個(gè)問(wèn)題的原因 問(wèn)題描述 在使用Koa-router作為路由遇到了一個(gè)優(yōu)先級(jí)問(wèn)題.如下代碼 // routerPage.js file const router = re...
摘要:第三篇,有關(guān)生態(tài)中比較重要的一個(gè)中間件第一篇源碼閱讀第二篇源碼閱讀與是什么首先,因?yàn)槭且粋€(gè)管理中間件的平臺(tái),而注冊(cè)一個(gè)中間件使用來(lái)執(zhí)行。這里寫(xiě)入的多個(gè)中間件都是針對(duì)該生效的。 第三篇,有關(guān)koa生態(tài)中比較重要的一個(gè)中間件:koa-router 第一篇:koa源碼閱讀-0 第二篇:koa源碼閱讀-1-koa與koa-compose koa-router是什么 首先,因?yàn)閗oa是一個(gè)管...
摘要:代碼結(jié)構(gòu)執(zhí)行流程上面兩張圖主要將的整體代碼結(jié)構(gòu)和大概的執(zhí)行流程畫(huà)了出來(lái),畫(huà)的不夠具體。那下面主要講中的幾處的關(guān)鍵代碼解讀一下。全局的路由參數(shù)處理的中間件組成的對(duì)象。 代碼結(jié)構(gòu) showImg(https://segmentfault.com/img/remote/1460000007468236?w=1425&h=1772); 執(zhí)行流程 showImg(https://segmentf...
摘要:四路由注冊(cè)構(gòu)造函數(shù)首先看了解一下構(gòu)造函數(shù)限制必須采用關(guān)鍵字服務(wù)器支持的請(qǐng)求方法,后續(xù)方法會(huì)用到保存前置處理函數(shù)存儲(chǔ)在構(gòu)造函數(shù)中初始化的和屬性最為重要,前者用來(lái)保存前置處理函數(shù),后者用來(lái)保存實(shí)例化的對(duì)象。 一、前言 ??Koa為了保持自身的簡(jiǎn)潔,并沒(méi)有捆綁中間件。但是在實(shí)際的開(kāi)發(fā)中,我們需要和形形色色的中間件打交道,本文將要分析的是經(jīng)常用到的路由中間件 -- koa-router。 ??...
路由koa-router——MVC 中重要的環(huán)節(jié):Url 處理器 ?? iKcamp 制作團(tuán)隊(duì) 原創(chuàng)作者:大哼、阿干、三三、小虎、胖子、小哈、DDU、可木、晃晃 文案校對(duì):李益、大力萌、Au、DDU、小溪里、小哈 風(fēng)采主播:可木、阿干、Au、DDU、小哈 視頻剪輯:小溪里 主站運(yùn)營(yíng):給力xi、xty 教程主編:張利濤 視頻地址:https://www.cctalk.com/v/151...
閱讀 2962·2021-10-18 13:33
閱讀 846·2019-08-30 14:20
閱讀 2633·2019-08-30 13:14
閱讀 2524·2019-08-29 18:38
閱讀 2892·2019-08-29 16:44
閱讀 1216·2019-08-29 15:23
閱讀 3491·2019-08-29 13:28
閱讀 1918·2019-08-28 18:00