摘要:改造成服務類接下來在這個基礎上改造一下,把封裝成一個對象。新建,測試代碼如下封裝上下文對象為了實現類似那種這樣的方式,先來新建個文件,,。
下面給大家帶來:封裝一個簡單的 Koa
Koa 是基于 Node.js 平臺的下一代 web 開發框架
Koa 是一個新的 web 框架,可以快速而愉快地編寫服務端應用程序,本文將跟大家一起學習:封裝一個簡單的 Koa
一個簡單的 http 服務
使用 node 提供的 http 模塊,可以很容易的實現一個基本的 http 服務器,新建一個 application.js 文件,內容如下:
const http = require("http") const server = http.createServer((req, res) => { res.end("Hello, Fq!") }) server.listen(8080, () => { console.info("Server is running at 8080") })
之后通過 node 來啟動這個腳本,打開瀏覽器 輸入地址 localhost:8080,即可訪問。
改造成服務類
接下來在這個基礎上改造一下,把 server 封裝成一個對象。
const http = require("http") class Application () { constructor () {} use (cb) { this.callback = cb } listen (...args) { const server = http.createServer((req, res) => { this.callback(req, res) }) server.listen(...args) } } module.exports = Application
新建 server.js ,測試代碼如下:
const Koa = require("./application.js") const app = new Koa() app.use((req, res) => { res.end("Hello, Fq!") }) app.listen(8080, () => { console.log("Server started!") })
封裝上下文對象
為了實現類似 Koa 那種 ctx.xxx 這樣的方式,先來新建3個文件:request.js,response.js,context.js 。
// request.js 以 url 為例: const request = { get url () { return this.req.url } } module.exports = request // response.js const reponse = { get body () { return this._body }, set body (val) { this._body = val } } module.exports = reponse // context.js const context = { get url () { return this.request.url }, get body () { return this.response.body }, set body (val) { this.response.body = val } } module.exports = context
整合上下文對象到服務類
可能看到上面3個對象,會有點迷糊的感覺,下面就把這3個對象添加到 Application 類中:
const http = require("http") const request = require("./require.js") const response = require("./response.js") const context = require("./context.js") class Application { constructor () { // 先把這3個屬性添加到構造函數中 this.context = context this.request = request this.response = response } use (cb) { this.callback = cb } createCtx (req, res) { // 新建 ctx 對象,并且繼承于 context const ctx = Object.create(this.context) // 像 ctx 對象添加兩個屬性 request response ctx.request = Object.create(this.request) ctx.response = Object.create(this.response) // 像 ctx 添加 req res 屬性,同時掛載到 response request 對象上 // req res 為 nodejs http 模塊的 原生對象 ctx.req = ctx.request.req = req ctx.res = ctx.response.res = res return ctx } listen (...args) { // 這里改造成 異步形式 const server = http.createServer(async (req, res) => { const ctx = this.createCtx(req, res) await this.callback(ctx) ctx.res.end(ctx.body) }) server.listen(...args) } } module.exports = Application
修改 server.js 文件,再次測試:
const Koa = require("./application.js")
const app = new Koa()
app.use(async (ctx) => {
ctx.body = ctx.url
})
app.listen(8080, () => {
console.log("Server started!")
})
串聯中間件
到此為止,咱們寫的 Koa 只能使用一個中間件,而且還不涉及到異步,下面咱們就一起來看看 Koa 中最核心的 compose 函數,是如何把各個中間件串聯起來的。
為了更容易的理解,先來寫一個同步版本的,依次執行 fn1, fn2:
const fn1 = x => Math.pow(x, 2) const fn2 = x => 2 * x function compose (middlewares) { return (x) => { let ret = middlewares[0](x) for (let i=1; i上面代碼可以直接在瀏覽器中測試結果。
那么如果 fn1 fn2 中如果有異步操作,應該如何處理呢,實際上只需要使用 Promise 改造一下 compose 的邏輯即可。
首先實現一個測試用休眠函數:
const sleep = (duratioin = 2000) => new Promise((resolve) => { setTimeout(resolve, duratioin) })其次準備3個測試用異步函數,最終效果是實現一個洋蔥圈模型:
const fn1 = async (next) => { console.log("fn1 start 休眠2秒") await sleep() await next() console.log("fn1 over") } const fn2 = async (next) => { console.log("fn2 start 休眠3秒") await sleep(3000) await next() console.log("fn2 duration....") await sleep(1000) console.log("fn2 over") } const fn3= async (next) => { console.log("fn3 start") await sleep() console.log("fn3 over") }執行的順序為 fn1 > fn2 > fn3 > fn2 > fn1
最后就是主角 componsefunction compose (middlewares) { return (context) => { return dispatch(0) function dispatch (i) { const fn = middlewares[i] if (!fn) return Promise.resolve() return Promise.resolve(fn(function next () { // await 的本質就是 一個返回 Promise 對象的函數 // 所以這里一定要 return return dispatch(i+1) })) } } }測試用例:
const fn = compose([fn1, fn2, fn3])
fn()效果如下圖:
整合compose到Server
廢話不說,直接上代碼:class Application { constructor () { this.context = context this.request = request this.response = response this.middlewares = [] } use (middleware) { this.middlewares.push(middleware) return this } createCtx (req, res) { const ctx = Object.create(this.context) ctx.request = Object.create(this.request) ctx.response = Object.create(this.response) ctx.req = ctx.request.req = req ctx.res = ctx.response.res = res return ctx } compose (middlewares) { return ctx => { return dispatch(0) function dispatch (index) { const fn = middlewares[index++] if (!fn || typeof fn !== "function") { return Promise.resolve() } return Promise.resolve(fn(ctx, next)) function next () { return dispatch(index) } } } } listen (...rest) { const server = http.createServer(async (req, res) => { const ctx = this.createCtx(req, res) const fn = this.compose(this.middlewares) await fn(ctx) ctx.res.end(ctx.body) }) server.listen(...rest) } } module.exports = Application下面可以測試一下了~
const Koa = require("./application.js") const app = new Koa() const sleep = (time) => new Promise((resolve, reject) => { setTimeout(resolve, time || 2000) }) app.use(async (ctx, next) => { ctx.body = "Hello" await sleep() await next() ctx.body += "q!" }) app.use(async (ctx, next) => { ctx.body += ", My name is" await sleep() await next() }) app.use(async (ctx, next) => { ctx.body += " F" }) app.listen(8080, () => { console.log("Server started!") })——到此為止,一個簡單的 Koa 就實現完畢了,是不是 so easy ?
——以上是筆者歸納總結,如有誤之處,歡迎指出。
原創: 付強 想要關注更多作者文章可關注:微信訂閱號ID:Miaovclass
微信訂閱號“妙味前端”,為您帶來優質前端技術干貨;
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/53131.html
摘要:目前來看,團隊內部前端項目已全面實施組件化開發。層疊樣式保佑不要污染別的在前端,一般一個組件必須要有骨架和裝飾的以及邏輯。事件綁定條件判斷秒后改變,會自動變更。循環姓名年齡增加修改移除的變更也能監聽到,能夠自動觸發的變更。 目前來看,團隊內部前端項目已全面實施組件化開發。組件化的好處太多,如:按需加載、可復用、易維護、可擴展、少挖坑、不改組件代碼直接切成服務器端渲染(如Nuclear組...
摘要:只能在不同的時候選用不同的假設和不同的理論來解釋問題,許來西的文章講到科學一定程度上通過放棄一貫性換取了實用性,放棄自洽性換取了它洽性。然而遺憾的是本身只提供了模塊和洋蔥模型的最小封裝。 在寫干貨之前,我想先探(qiang)討(diao)兩個問題,模式的局限性?模式有什么用? 最近看到一篇文章對我啟發很大,許來西在知乎的回答《哲學和科學有什么關聯?》,全篇較長,這里摘錄我要引出的一點:...
摘要:前言什么這是一篇源碼解讀文章那一定很枯燥不看。通過利用函數,幫你丟棄回調函數,并有力地增強錯誤處理。并沒有捆綁任何中間件,而是提供了一套優雅的方法,幫助您快速而愉快地編寫服務端應用程序。 showImg(https://segmentfault.com/img/bVNQYf?w=1020&h=790); 前言 什么?這是一篇源碼解讀文章 ? 那一定很枯燥!不看。 我把 Koa 的核心實...
摘要:背景比特幣說好的分叉最后卻分叉不成,如今算力又不夠,于是比特現金想篡位沒一個星期就漲了快倍,錯過這趟快車甚是后悔,于是打算寫一個可不定期推送最新消息的微信公眾號。既然是利用微信這個平臺載體,當然要熟悉微信的,遂封裝了一下。 背景:比特幣說好的segwit2x分叉最后卻分叉不成,如今算力又不夠,于是比特現金想篡位? 沒一個星期就漲了快10倍,錯過這趟快車甚是后悔,于是打算寫一個可不定期推...
閱讀 1266·2021-11-24 09:39
閱讀 1517·2021-09-07 09:59
閱讀 3479·2019-08-30 15:54
閱讀 2474·2019-08-30 11:00
閱讀 2669·2019-08-29 15:06
閱讀 2160·2019-08-26 13:52
閱讀 427·2019-08-26 13:24
閱讀 2489·2019-08-26 12:20