摘要:詳細代碼如下追蹤賦值里面的是子路由設計子路由設計這個比較簡單,每個子路由維護一個路由監聽列表,然后通過調用的函數添加到主路由列表上。
前言
鑒于之前使用express和koa的經驗,這兩天想嘗試構建出一個koa精簡版,利用最少的代碼實現koa和koa-router,同時也梳理一下Node.js網絡框架開發的核心內容。
實現后的核心代碼不超過300行,源代碼配有詳細的注釋。
核心設計 API調用在mini-koa的API設計中,參考koa和koa-router的API調用方式。
Node.js的網絡框架封裝其實并不復雜,其核心點在于http/https的createServer方法上,這個方法是http請求的入口。
首先,我們先回顧一下用Node.js來啟動一個簡單服務。
// https://github.com/qzcmask/mini-koa/blob/master/examples/simple.js const http = require("http") const app = http.createServer((request, response) => { response.end("hello Node.js") }) app.listen(3333, () => { console.log("App is listening at port 3333...") })路由原理
既然我們知道Node.js的請求入口在createServer方法上,那么我們可以在這個方法中找出請求的地址,然后根據地址映射出監聽函數(通過get/post等方法添加的路由函數)即可。
其中,路由列表的格式設計如下:
// binding的格式 { "/": [fn1, fn2, ...], "/user": [fn, ...], ... } // fn/fn1/fn2的格式 { method: "get/post/use/all", fn: "路由處理函數" }難點分析 next()方法設計
我們知道在koa中是可以添加多個url監聽函數的,其中決定是否傳遞到下一個監聽函數的關鍵在于是否調用了next()函數。如果調用了next()函數則先把路由權轉移到下一個監聽函數中,處理完畢再返回當前路由函數。
在mini-koa中,我把next()方法設計成了一個返回Promise fullfilled的函數(這里簡單設計,不考慮next()傳參的情況),用戶如果調用了該函數,那么就可以根據它的值來決定是否轉移路由函數處理權。
判斷是否轉移路由函數處理權的代碼如下:
let isNext = false const next = () => { isNext = true return Promise.resolve() } await router.fn(ctx, next) if (isNext) { continue } else { // 沒有調用next,直接中止請求處理函數 return }use()方法設計
mini-koa提供use方法,可供擴展日志記錄/session/cookie處理等功能。
use方法執行的原理是根據請求地址在執行特定路由函數之前先執行mini-koa調用use監聽的函數。
所以這里的關鍵點在于怎么找出use監聽的函數列表,假設現有監聽情況如下:
app.use("/", fn1) app.use("/user", fn2)
如果訪問的url是/user/add,那么fn1和fn2都必須要依次執行。
我采取的做法是先根據/字符來分割請求url,然后循環拼接,查看路由綁定列表(binding)中有沒有要use的函數,如果發現有,添加進要use的函數列表中,沒有則繼續下一次循環。
詳細代碼如下:
// 默認use函數前綴 let prefix = "/" // 要預先調用的use函數列表 let useFnList = [] // 分割url,使用use函數 // 比如item為/user/a/b映射成[("user", "a", "b")] const filterUrl = url.split("/").filter(item => item !== "") // 該reduce的作用是找出本請求要use的函數列表 filterUrl.reduce((cal, item) => { prefix = cal if (this.binding[prefix] && this.binding[prefix].length) { const filters = this.binding[prefix].filter(router => { return router.method === "use" }) useFnList.push(...filters) } return ( "/" + [cal, item] .join("/") .split("/") .filter(item => item !== "") .join("/") ) }, prefix)ctx.body響應
通過ctx.body = "響應內容"的方式可以響應http請求。它的實現原理是利用了ES6的Object.defineProperty函數,通過設置它的setter/getter函數來達到數據追蹤的目的。
詳細代碼如下:
// 追蹤ctx.body賦值 Object.defineProperty(ctx, "body", { set(val) { // set()里面的this是ctx response.end(val) }, get() { throw new Error(`ctx.body can"t read, only support assign value.`) } })子路由mini-koa-router設計
子路由mini-koa-router設計這個比較簡單,每個子路由維護一個路由監聽列表,然后通過調用mini-koa的addRoutes函數添加到主路由列表上。
mini-koa的addRoutes實現如下:
addRoutes(router) { if (!this.binding[router.prefix]) { this.binding[router.prefix] = [] } // 路由拷貝 Object.keys(router.binding).forEach(url => { if (!this.binding[url]) { this.binding[url] = [] } this.binding[url].push(...router.binding[url]) }) }用法
使用示例如下,源代碼可以在github上找到:
// examples/server.js // const { Koa, KoaRouter } = require("mini-koa") const { Koa, KoaRouter } = require("../index") const app = new Koa() // 路由用法 const userRouter = new KoaRouter({ prefix: "/user" }) // 中間件函數 app.use(async (ctx, next) => { console.log(`請求url, 請求method: `, ctx.req.url, ctx.req.method) await next() }) // 方法示例 app.get("/get", async ctx => { ctx.body = "hello ,app get" }) app.post("/post", async ctx => { ctx.body = "hello ,app post" }) app.all("/all", async ctx => { ctx.body = "hello ,/all 支持所有方法" }) // 子路由使用示例 userRouter.post("/login", async ctx => { ctx.body = "user login success" }) userRouter.get("/logout", async ctx => { ctx.body = "user logout success" }) userRouter.get("/:id", async ctx => { ctx.body = "用戶id: " + ctx.params.id }) // 添加路由 app.addRoutes(userRouter) // 監聽端口 app.listen(3000, () => { console.log("> App is listening at port 3000...") })總結
挺久沒有造輪子了,這次突發奇想造了個精簡版的koa,雖然跟常用的koa框架有很大差別,但是也實現了最基本的API調用和原理。
造輪子是一件難能可貴的事,程序員在學習過程中不應該崇尚拿來主義,學習到一定程度后,要秉持能造就造的態度,去嘗試理解和挖掘輪子背后的原理和思想。
當然,通常來說,自己造的輪子本身不具備多大的實用性,沒有經歷過社區大量的測試和實際應用場景的打磨,但是能加深自己的理解和提高自己的能力也是一件值得堅持的事。
人生是一段不斷攀登的高峰,只有堅持向前,才能看到新奇的東西。
最后附上項目的Github地址,歡迎Star或Fork支持,謝謝。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103631.html
摘要:于是翻遍與各大網站,都沒找到一個好用的輕一點的腳手架,也找不到一個清晰些的搭建介紹。現在把搭建過程介紹下,看能不能方便下入門的同學。創建一個文件夾,命名。記得先裝好以上版本一路回車,根據提示輸入信息。但這只是初步的搭建了下。 前幾天想寫個小爬蟲程序,準備后端就用koa2。于是翻遍github與各大網站,都沒找到一個好用的、輕一點的koa2腳手架,也找不到一個清晰些的搭建介紹。githu...
路由koa-router——MVC 中重要的環節:Url 處理器 ?? iKcamp 制作團隊 原創作者:大哼、阿干、三三、小虎、胖子、小哈、DDU、可木、晃晃 文案校對:李益、大力萌、Au、DDU、小溪里、小哈 風采主播:可木、阿干、Au、DDU、小哈 視頻剪輯:小溪里 主站運營:給力xi、xty 教程主編:張利濤 視頻地址:https://www.cctalk.com/v/151...
摘要:使用承諾和異步功能來擺脫回調地獄的應用程序,并簡化錯誤處理。它暴露了自己的和對象,而不是的和對象。因此,可被視為的模塊的抽象,其中是的應用程序框架。這使得中間件對于整個堆棧而言不僅僅是最終應用程序代碼,而且更易于書寫,并更不容易出錯。 Koa 與 Express 此系列文章的應用示例已發布于 GitHub: koa-docs-Zh-CN. 可以 Fork 幫助改進或 Star 關注更新...
閱讀 1507·2021-11-25 09:43
閱讀 4057·2021-11-15 11:37
閱讀 3192·2021-08-17 10:13
閱讀 3503·2019-08-30 14:16
閱讀 3535·2019-08-26 18:37
閱讀 2489·2019-08-26 11:56
閱讀 1128·2019-08-26 10:42
閱讀 609·2019-08-26 10:39