摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。使用其實某個框架閱讀源碼的時候,首先我們要會去用這個框架,因為用了我們才知道,某個是怎么用,哪里有坑,哪里設計的精妙。
閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。大到架構設計,小到可取的命名風格,還有設計模式、實現某類功能使用到的數據結構和算法等等。使用koa
其實某個框架閱讀源碼的時候,首先我們要會去用這個框架,因為用了我們才知道,某個API是怎么用,哪里有坑,哪里設計的精妙。
下面我們就簡單用一下koa這個框架,如下代碼
const Koa = require("koa") const app = new Koa() app.use(async (ctx, next) => { ctx.body = "Hello World" }) app.listen(4002)
運行結果
瓦特??這個服務會涉及到從請求到響應返回數據,就這幾行代碼?? 是的,你沒有看錯,就是單單這幾行代碼就可以搭建了一個服務器。
下面我們看看一探究竟。
閱讀源碼去到node_modules文件夾下找到koa模塊,先喵幾眼README.md文件,里面介紹了koa的一些安裝、用法、插件等等,這里我們跳過,然后轉到package.json如下圖
看到package.json里面的"main": "lib/application.js"沒錯,這就是我們的入口,在lib文件夾下面,我們看到里面有application.js、context.js、requrest.js和response.js。下面經過我修改簡化去掉注釋application.js就只有68行代碼。閱讀起來可以說是非常簡單了。如下圖:
第一步是我們引入各種主要依賴
// 引入有很多 我只挑我閱讀主要框架的代碼模塊 const response = require("./response"); // 處理response對象 const compose = require("koa-compose"); // 合并處理中間件函數 const context = require("./context"); // 整合處理context對象 const request = require("./request"); // 整合處理request對象 const http = require("http"); // node的 http原生模塊
以上就是我們的主要依賴
在Application的對象中,有constructor函數,這個主要是初始化Application對象,生成context對象、request對象、response對象,
module.exports = class Application extends Emitter { // 初始化 Application constructor() { super(); // 繼承Emitter this.middleware = []; // 初始化middleware為空數組 this.context = Object.create(context); // 生成context對象 this.request = Object.create(request); // 生成request對象 this.response = Object.create(response); // 生成response對象 } }
閱讀源碼,我們先不要去扣細節,比如說Object.create(context)生產的對象是什么?this.request對象下面又有什么東西???,我們現在主要知道的是、this.context是能獲取或者設置請求和響應的信息的一個對象,。this.request是請求的對象、里面可以設置或者獲取請求信息的一個對象、this.response是響應請求對象、里面可以設置或者獲取響應參數和值的一個對象。大概先了解就可以了。繼續往下看。
在上面運用的時候,用到了app.use(fn)和app.listen(4002) 我們看看,源碼里面試這樣子的
module.exports = class Application extends Emitter { // 初始化 Application constructor() { ... } listen(...args) { const server = http.createServer(this.callback()); return server.listen(...args); } use(fn) { this.middleware.push(fn); return this; }
上面的代碼很簡單 use函數就是把傳入的fn 推入到this.middleware的數組中,然后返回this,方便鏈式調用。
然后在listen里面用node原生的http模塊創建一個server,在這里順便說一下,原生 http創建一個服務是這樣子滴
const http = http.createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("okay"); }); http.listen(8000)
繼續看代碼 ,在創建服務的時候,參數里面調用了一個this.callback()函數,下面我們看看這個函數究竟是怎么樣子的。
module.exports = class Application extends Emitter { // 初始化 Application constructor() { ... } listen(...args) { ... } use(fn) { ... } callback() { const fn = compose(this.middleware); // 集中處理中間件數組 const handleRequest = (req, res) => { const ctx = this.createContext(req, res); // 整合req、res、context、request、response return this.handleRequest(ctx, fn); // 返回handleRequest }; return handleRequest; } handleRequest(ctx, fnMiddleware) { const handleResponse = () => respond(ctx); // 最終響應函數 return fnMiddleware(ctx).then(handleResponse) // 處理完中間件,然后傳到下一響應函數 } // 創建整合新的 context. createContext(req, res) { const context = Object.create(this.context); const request = context.request = Object.create(this.request); const response = context.response = Object.create(this.response); context.app = request.app = response.app = this; context.req = request.req = response.req = req; context.res = request.res = response.res = res; request.ctx = response.ctx = context; request.response = response; response.request = request; context.originalUrl = request.originalUrl = req.url; context.cookies = new Cookies(req, res, { keys: this.keys, secure: request.secure }); request.ip = request.ips[0] || req.socket.remoteAddress || ""; context.accept = request.accept = accepts(req); context.state = {}; return context; } };
上面我們可以看出在callback函數里面有一個const fn = compose(this.middleware); 這個函數就是把this.middleware數組傳進去,然后集中處理中間件,然后會返回處理完中間件的fn。
繼續下一行
const handleRequest = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); };
繼續進入到handleRequest函數里面的const ctx = this.createContext(req, res);這個把原生的http的請求對象req和響應對象res作為參數傳進去,然后在createContext函數(看上面最大那坨代碼)在里面,把this.request、this.response、this.context、請求對象req、響應對象res都整,做各種整合、處理得到新的context對象返回出去。
也就是強大的ctx,得到ctx之后,下一行返回return this.handleRequest(ctx, fn);
this.handleRequest(ctx, fn)代碼如下
handleRequest(ctx, fnMiddleware) { const handleResponse = () => respond(ctx); return fnMiddleware(ctx).then(handleResponse).catch(onerror); }
這個函數 就是處理完中間件處理之后的返回的函數把ctx傳下去,最后流通到respond(ctx);這個函數,
那么我們看看這個函數被我簡化后是怎么樣子的,如下
// 一些容錯判斷或者提示我全部刪了 function respond(ctx) { const res = ctx.res; let body = ctx.body; res.end(body); }
通過ctx拿到響應對象,和響應值、通過end方法會通知服務器,所有響應頭和響應主體都已被發送,即服務器將其視為已完成。看上面原生的http的服務方法。
最后附上一個流程圖
這個只是介紹application整個流程,還有很多細節都沒有一一介紹到,比如、創建context、request、response對象是怎么樣子的呀?中間件是如何集中層層深入處理然后返回的呀?等等這些細節都會在下一篇會講到(最近公司業務非常忙,不知道到猴年馬月)。
寫的不好的地方,讓大家賤笑了。
然后最后安利一波博客,喜歡的小哥哥小姐姐可以star 喲
websit: https://github.com/naihe138/naice-blog
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95040.html
摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。使用其實某個框架閱讀源碼的時候,首先我們要會去用這個框架,因為用了我們才知道,某個是怎么用,哪里有坑,哪里設計的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個框架的設計思想。大到架構設計,小到可取的命名風格,還有設計模式、實現某類功能使用到的數據結構和算法等等。 使用koa 其實某個框架閱讀源碼的時候,首...
摘要:設置了一個出事索引值調用函數,開始時候傳進去聲明函數,把傳進來的賦值給拿出第個中間件函數,賦值給判斷如果等于的長度就把賦值給如果是假的返回一個同時執行,也就是中間件,把函數傳遞到里面遞歸調用自己上面的代碼是這個部分的精華。 koa的中間件執行的流程控制,代碼的是非常精妙的。由下面的一張洋蔥模型的圖來形容,記住這張圖。 showImg(https://segmentfault.com/i...
摘要:前言自從上次在掘金發布年山地人的前端完整自學計劃講一個站主山地人的天前端自學故事以來,一眨眼山地人老哥在站做主已經有天了。所以這個體系里的一些框架包括也是山地人年自學計劃的一部分。月底,山地人老哥開啟了的兩個專題。 前言 自從上次在掘金發布【2019年山地人的前端完整自學計劃——講一個B站UP主山地人的40天前端自學故事】 以來,一眨眼山地人老哥在B站做Up主已經有85天了。 時隔一個...
摘要:本打算教一步步實現,因為要解釋的太多了,所以先簡化成版本,從實現部分功能到閱讀源碼,希望能讓你好理解一些。 本打算教一步步實現koa-router,因為要解釋的太多了,所以先簡化成mini版本,從實現部分功能到閱讀源碼,希望能讓你好理解一些。希望你之前有讀過koa源碼,沒有的話,給你鏈接 最核心需求-路由匹配 router最重要的就是路由匹配,我們就從最核心的入手 router.get...
閱讀 2247·2021-11-25 09:43
閱讀 2934·2019-08-30 15:52
閱讀 1885·2019-08-30 15:44
閱讀 975·2019-08-30 10:58
閱讀 754·2019-08-29 18:43
閱讀 3208·2019-08-29 18:36
閱讀 2310·2019-08-29 17:02
閱讀 1447·2019-08-29 17:01