此係列文章的應用示例已發佈於 GitHub: koa-docs-Zh-CN. 能夠 Fork 幫助改進或 Star 關注更新. 歡迎 Star.前端
本指南涵蓋的 Koa 主題不與 API 直接相關,例如編寫中間件的最佳作法和應用程序結構建議。在這些例子中,咱們使用 async 函數做爲中間件 - 您也可使用 commonFunction 或generatorFunction,這將些所不一樣。node
Koa 中間件是簡單的函數,它返回一個帶有簽名 (ctx, next) 的MiddlewareFunction
。當中間件運行時,它必須手動調用 next()
來運行 「下游」 中間件。git
例如,若是你想要跟蹤經過添加 X-Response-Time
頭字段經過 Koa 傳播請求須要多長時間,則中間件將以下所示:github
async function responseTime(ctx, next) { const start = Date.now(); await next(); const ms = Date.now() - start; ctx.set('X-Response-Time', `${ms}ms`); } app.use(responseTime);
若是您是前端開發人員,您能夠將 next();
以前的任意代碼視爲「捕獲」階段,這個簡易的 gif 說明了 async 函數如何使咱們可以恰當地利用堆棧流來實現請求和響應流:promise
X-Response-Time
頭字段接下來,咱們將介紹建立 Koa 中間件的最佳作法。markdown
本節介紹中間件創做最佳實踐,例如中間件接受參數,命名中間件進行調試等等。app
當建立公共中間件時,將中間件包裝在接受參數的函數中,遵循這個約定是有用的,容許用戶擴展功能。即便您的中間件 不 接受任何參數,這仍然是保持統一的好方法。dom
這裏咱們設計的 logger
中間件接受一個 format
自定義字符串,並返回中間件自己:koa
function logger(format) { format = format || ':method ":url"'; return async function (ctx, next) { const str = format .replace(':method', ctx.method) .replace(':url', ctx.url); console.log(str); await next(); }; } app.use(logger()); app.use(logger(':method :url'));
命名中間件是可選的,可是在調試中分配名稱頗有用。異步
function logger(format) { return async function logger(ctx, next) { }; }
有時您想要將多箇中間件 「組合」 成一個單一的中間件,便於重用或導出。你可使用 koa-compose
const compose = require('koa-compose'); async function random(ctx, next) { if ('/random' == ctx.path) { ctx.body = Math.floor(Math.random() * 10); } else { await next(); } }; async function backwards(ctx, next) { if ('/backwards' == ctx.path) { ctx.body = 'sdrawkcab'; } else { await next(); } } async function pi(ctx, next) { if ('/pi' == ctx.path) { ctx.body = String(Math.PI); } else { await next(); } } const all = compose([random, backwards, pi]); app.use(all);
中間件決定響應請求,並但願繞過下游中間件能夠簡單地省略 next()
。一般這將在路由中間件中,但這也能夠任意執行。例如,如下內容將以 「two」 進行響應,可是全部三個都將被執行,從而使下游的 「three」 中間件有機會操縱響應。
app.use(async function (ctx, next) { console.log('>> one'); await next(); console.log('<< one'); }); app.use(async function (ctx, next) { console.log('>> two'); ctx.body = 'two'; await next(); console.log('<< two'); }); app.use(async function (ctx, next) { console.log('>> three'); await next(); console.log('<< three'); });
如下配置在第二個中間件中省略了next()
,而且仍然會以 「two」 進行響應,然而,第三個(以及任何其餘下游中間件)將被忽略:
app.use(async function (ctx, next) { console.log('>> one'); await next(); console.log('<< one'); }); app.use(async function (ctx, next) { console.log('>> two'); ctx.body = 'two'; console.log('<< two'); }); app.use(async function (ctx, next) { console.log('>> three'); await next(); console.log('<< three'); });
當最遠的下游中間件執行 next();
時,它其實是一個 noop 函數,容許中間件在堆棧中的任意位置正確組合。
Async 方法和 promise 來自 Koa 的底層,可讓你編寫非阻塞序列代碼。例如,這個中間件從 ./docs
讀取文件名,而後在將給 body 指定合併結果以前並行讀取每一個 markdown 文件的內容。
const fs = require('fs-promise'); app.use(async function (ctx, next) { const paths = await fs.readdir('docs'); const files = await Promise.all(paths.map(path => fs.readFile(`docs/${path}`, 'utf8'))); ctx.type = 'markdown'; ctx.body = files.join(''); });
Koa 以及許多構建庫,支持來自 debug 的 DEBUG 環境變量,它提供簡單的條件記錄。
例如,要查看全部 koa 特定的調試信息,只需經過 DEBUG=koa*
,而且在啓動時,您將看到所使用的中間件的列表。
$ DEBUG=koa* node --harmony examples/simple koa:application use responseTime +0ms koa:application use logger +4ms koa:application use contentLength +0ms koa:application use notfound +0ms koa:application use response +0ms koa:application listen +0ms
因爲 JavaScript 在運行時沒有定義函數名,你也能夠將中間件的名稱設置爲 ._name
。當你沒法控制中間件的名稱時,這頗有用。例如:
const path = require('path'); const serve = require('koa-static'); const publicFiles = serve(path.join(__dirname, 'public')); publicFiles._name = 'static /public'; app.use(publicFiles);
如今,在調試時不僅會看到 「serve」,你也會看到:
Now, instead of just seeing "serve" when debugging, you will see:
koa:application use static /public +0ms
若是這篇文章對您有幫助, 感謝 下方點贊 或 Star GitHub: koa-docs-Zh-CN 支持, 謝謝.