本文由郝晨光整理總結(jié)并編寫沐寺,未經(jīng)允許禁止轉(zhuǎn)載。
前言
學(xué)習(xí)koa盖奈,我之前學(xué)習(xí)過express混坞,但是在使用express的時候,還是一直使用的回調(diào)函數(shù)的方式來處理異步,現(xiàn)在想想真是恐怖究孕,后來了解到koa這個框架啥酱,它相對于express來說,小巧了很多厨诸,對于異步的處理也變得更加優(yōu)雅了镶殷。用官方的話來說,koa是基于Node.js平臺的下一代web平臺開發(fā)框架微酬。
Image 3.png
正文
首先肯定要先開始一個新的項目绘趋,新建一個文件夾
在文件夾目錄中,使用命令提示符執(zhí)行
npm init -y
颗管,初始化package.json
文件陷遮;-
安裝
- Koa 依賴 node - v7.6.0 或 ES2015及更高版本和 async 方法支持。
- 先查看node的版本
node -v
; - 然后執(zhí)行
npm install koa
進行安裝垦江;
-
使用
- 新建index.js帽馋;
- 先上官網(wǎng)上最經(jīng)典的hello wrold案例;
const Koa = require('koa'); const app = new Koa(); app.use(async ctx => { ctx.body = 'hello word!' }) app.listen(8000, () => { console.log('Koa server listen in http://localhost:8000/'); })
- 打開瀏覽器疫粥,輸入
localhost:8000/
茬斧,就可以看到我們的頁面上顯示了hello world!; - 那我們的第一個koa應(yīng)用就已經(jīng)運行起來了~。
-
讀取 HTML 頁面
- 在Koa中梗逮,我們?nèi)绻褂?HTML 應(yīng)該怎么做呢项秉?
- 在原生nodejs或者express中,我們使用的是fs模塊慷彤,然后使用回調(diào)函數(shù)娄蔼,一層套一層;
- 在Koa中底哗,我們依舊使用fs模塊岁诉,但是,要對fs模塊的讀取文件跋选,進行進一步的封裝涕癣;
- 在目錄下,我們新建一個
index.html
,并隨便寫一點東西前标; - 接著開始我們漸行漸遠(yuǎn)的程序生涯坠韩;
const Koa = require('koa'); const fs = require('fs'); const app = new Koa(); function readFile(url) { return new Promise((resolve, reject) => { fs.readFile(url, (err,file) => { if(err) { reject(err); }else { resolve(file); } }) }) } app.use(async ctx => { ctx.response.type = 'html'; // 重點 ctx.body = await readFile('index.html'); }) app.listen(8000, () => { console.log('Koa server listen in http://localhost:8000/'); }) // 本文由郝晨光整理總結(jié)并編寫,未經(jīng)允許禁止轉(zhuǎn)載炼列。
- 可以看到只搁,我們利用Promise將fs模塊的readFile方法進行了二次封裝,讀取了本地的index.html文件
- 重點的地方我已經(jīng)標(biāo)出來了俭尖,為什么要標(biāo)重點呢氢惋?
- 因為在Koa中洞翩,ctx.body默認(rèn)返回的類型是
text/plain
;而我們要返回html文件焰望,所以應(yīng)該在返回文件之前骚亿,設(shè)置好返回類型,讓客戶端可以正確的接收柿估; - 然后我們使用
async + await
組合循未,在數(shù)據(jù)讀取完成之后,返回讀取的文件秫舌; - 最后的妖,打開瀏覽器,可以看到瀏覽器上輸出的就是我們
index.html
文件中寫的內(nèi)容
-
讀取靜態(tài)資源
- html文件我們已經(jīng)可以正確的讀取并返回了足陨,在我們的頁面上嫂粟,避免不了會使用很多的靜態(tài)資源,例如css墨缘、js星虹、image、font等等镊讼;這些靜態(tài)資源我們應(yīng)該怎么處理呢宽涌?
- 在這里,我們使用一個Koa的中間件
koa-static
- 在命令提示符中執(zhí)行
npm install koa-static
蝶棋; - 接著書寫我們的程序
-
// ~~ 省略 ~~ const app = new Koa(); // 新建koa對象 const KoaStatic = require('koa-static'); // koa靜態(tài)文件讀取 app.use(KoaStatic(__dirname)); // 使用中間件 // ~~ 省略 ~~
- 接著卸亮,打開我們的瀏覽器,可以看到靜態(tài)資源已經(jīng)可以正確的請求到了玩裙;
- 當(dāng)然了兼贸,這個時候,我們可以把之前返回html文件時設(shè)置響應(yīng)類型的一步省略掉了吃溅,因為
koa-static
已經(jīng)幫我們做了這件事了溶诞。
-
定義路由
- 所謂的路由,就是根據(jù)不同的請求路徑决侈,響應(yīng)不同的內(nèi)容螺垢;
- 在Koa中,我們可以定義原生路由赖歌,也可以使用封裝好的路由中間件枉圃;
- 先看一下原生路由的寫法吧!
app.use(async ctx => { if(ctx.request.method==='GET') { // GET請求 switch (ctx.request.path) { case '/': // 匹配默認(rèn)路由 ctx.body = await readFile('index.html'); // 返回index.html文件 break; case '/about': // 匹配/about路由 ctx.body = 'about路由頁面'; // 返回about路由頁面 break; default: ctx.body = await readFile('index.html'); // 默認(rèn)返回index.html文件 break; } }else if(ctx.request.method==='POST') { // POST請求 switch (ctx.request.path) { default: ctx.body = 'post 請求'; // 響應(yīng)post請求 break; } } }); //本文由郝晨光整理總結(jié)并編寫俏站,未經(jīng)允許禁止轉(zhuǎn)載讯蒲。
-
可以看到我上邊寫的痊土,對請求方式以及路由進行了處理肄扎,在不同的請求方式,不同的請求路徑下,執(zhí)行不同的操作犯祠,響應(yīng)不同的結(jié)果旭等。
原生路由
-
路由中間件
- 我們使用原生方式定義路由,未免有些太過繁瑣衡载,并且代碼不便于閱覽和維護搔耕;所以,建議使用中間件的方式進行路由配置痰娱。
- 我這里使用的是 koa-router 這個中間件弃榨,當(dāng)然,koa的路由中間件不是只有這一種梨睁。
- 在命令提示符中安裝
npm install koa-router
; - 接著鲸睛,進行我們的程序生涯;
// ~~ 省略 ~~ const KoaRouter = require('koa-router'); const router = new KoaRouter(); router.get('/',async ctx => { ctx.body = await readFile('index.html'); }); router.get('/about',async ctx=> { ctx.body = 'about路由頁面' }); router.post('/form',async ctx => { ctx.body = 'post請求' }); app.use(router.routes()); // ~~ 省略 ~~
- 打開頁面坡贺,并進行路由切換官辈,可以看到?jīng)]有任何問題;
-
但是這樣所有的路由配置都寫在了index.js中遍坟,不利于維護拳亿,所以我們要將路由內(nèi)容和公用方法提取出來。
路由中間件
-
代碼模塊化
- 首先對路由進行模塊化
- 在目錄下新建routes文件夾愿伴,并新建home.js肺魁,用來存放關(guān)于首頁的一些路由配置。
- 將上邊的路由代碼單獨寫到home.js中公般,并拋出万搔,如下:
- home.js
const KoaRouter = require('koa-router'); const router = new KoaRouter(); router.get('/',async ctx => { ctx.body = await readFile('index.html'); }); router.get('/about',async ctx=> { ctx.body = 'about 路由' }); router.post('/from',async ctx => { ctx.body = 'post請求' }); module.exports = router;
- index.js
const homeRouter = require('./routes/home'); app.use(KoaStatic(__dirname)); app.use(homeRouter.routes());
- 在index.js中亡哄,直接引入使用即可胆描,和原有代碼沒有任何區(qū)別。
- 需要注意的是凤价,對于功能中間件刽虹,例如
koa-static
這種酗捌,我們應(yīng)該放在路由中間件之前。 - 否則的話可能會出錯涌哲,在路由中不能正確的讀取靜態(tài)文件等胖缤。
-
二級路由
- 已經(jīng)學(xué)會了一級路由的定義,我們趁熱打鐵阀圾,學(xué)習(xí)二級路由
- 在routes目錄下哪廓,新建user.js,定義用戶路由
// user.js const KoaRouter = require('koa-router'); const router = new KoaRouter({ prefix: '/user' }); router.get('/',async ctx => { ctx.body = '用戶界面' }); router.get('/:id',async ctx => { ctx.body = '用戶詳情'+ctx.params.id; }); router.post('/login',async ctx => { ctx.body = '用戶注冊成功初烘!' }); module.exports = router; // index.js // ~~ 省略 ~~ const userRouter = require('./routes/user'); // ~~ 省略 ~~ app.use(userRouter.routes());
-
接著涡真,打開瀏覽器分俯,我們測試一下我們的二級路由,是沒有任何問題的哆料。
二級路由.gif
-
post請求處理
- 我們都知道缸剪,使用post請求一般用來提交表單,或者修改數(shù)據(jù)等东亦。而post請求中的數(shù)據(jù)杏节,存放在請求體中,使用原生Node的方法的話典阵,我們需要監(jiān)聽原生req對象上的data方法以及end方法奋渔,并將數(shù)據(jù)格式轉(zhuǎn)化。而express中壮啊,我們有一個
body-parser
的插件可以使用卒稳。 - 那么在Koa中,我們應(yīng)該使用什么呢他巨?
- 在Koa中充坑,我們使用
koa-bodyparser
這個中間件來處理post請求的數(shù)據(jù) - 在命令提示符安裝
npm install koa-bodyparser
; - 接著我們在index.js中使用這個中間件;
- 加入下面兩行代碼染突,與原先使用
koa-static
中間件位置一樣即可捻爷; const KoaBodyParser = require('koa-bodyparser'); app.use(KoaBodyParser());
- 使用了這個中間件,我們就可以在
ctx.request.body
中拿到post請求的數(shù)據(jù)了份企; router.post('/login',async ctx => { ctx.body = ctx.request.body; });
- 接著改造一下我們的html文件也榄,提交一下表單;
-
<form action="/user/login" method="post"> 姓名:<input type="text" name="name" autocomplete="off"/> <br> <input type="radio" name="sex" value="男"/>男 <input type="radio" name="sex" value="女"/>女 <input type="submit" value="提交form"> </form>
-
可以看到司志,表單提交正常甜紫,我們也正確的拿到了表單提交的數(shù)據(jù)。
Koa提交表單
-
- 我們都知道缸剪,使用post請求一般用來提交表單,或者修改數(shù)據(jù)等东亦。而post請求中的數(shù)據(jù)杏节,存放在請求體中,使用原生Node的方法的話典阵,我們需要監(jiān)聽原生req對象上的data方法以及end方法奋渔,并將數(shù)據(jù)格式轉(zhuǎn)化。而express中壮啊,我們有一個
-
CORS跨域配置
- 在我們現(xiàn)在的服務(wù)器生涯中骂远,避免不了要使用跨域囚霸,特別是CORS跨域。
- 那么激才,在Koa中拓型,我們應(yīng)該怎么去設(shè)置跨域呢?
- 我們可以使用中間件瘸恼,也可以使用原生方法手寫劣挫。
- 老規(guī)矩,先看原生方法东帅。
-
app.use(async (ctx, next) => { // 允許來自所有域名請求 ctx.set("Access-Control-Allow-Origin", "*"); // 這樣就能只允許 http://localhost:8080 這個域名的請求了 // ctx.set("Access-Control-Allow-Origin", "http://localhost:8080"); // 設(shè)置所允許的HTTP請求方法 ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE"); // 字段是必需的压固。它也是一個逗號分隔的字符串,表明服務(wù)器支持的所有頭信息字段. ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type"); // 服務(wù)器收到請求以后靠闭,檢查了Origin帐我、Access-Control-Request-Method和Access-Control-Request-Headers字段以后刘莹,確認(rèn)允許跨源請求,就可以做出回應(yīng)焚刚。 // Content-Type表示具體請求中的媒體類型信息 ctx.set("Content-Type", "application/json;charset=utf-8"); // 該字段可選。它的值是一個布爾值扇调,表示是否允許發(fā)送Cookie矿咕。默認(rèn)情況下,Cookie不包括在CORS請求之中狼钮。 // 當(dāng)設(shè)置成允許請求攜帶cookie時碳柱,需要保證"Access-Control-Allow-Origin"是服務(wù)器有的域名,而不能是"*"; ctx.set("Access-Control-Allow-Credentials", true); // 該字段可選熬芜,用來指定本次預(yù)檢請求的有效期莲镣,單位為秒。 // 當(dāng)請求方法是PUT或DELETE等特殊方法或者Content-Type字段的類型是application/json時涎拉,服務(wù)器會提前發(fā)送一次請求進行驗證 // 下面的的設(shè)置只本次驗證的有效時間瑞侮,即在該時間段內(nèi)服務(wù)端可以不用進行驗證 ctx.set("Access-Control-Max-Age", 300); /* CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段: Cache-Control鼓拧、Content-Language半火、Content-Type、Expires季俩、Last-Modified钮糖、Pragma。 */ // 需要獲取其他字段時酌住,使用Access-Control-Expose-Headers店归, // getResponseHeader('myData')可以返回我們所需的值 ctx.set("Access-Control-Expose-Headers", "myData"); await next(); })
- 原生方法來自:node.js 應(yīng)答跨域請求實現(xiàn)(以koa2-cors為例)
- 接著我們來看中間件吧!
- 在Koa2中酪我,我們使用
koa2-cors
這個中間件來設(shè)置CORS跨域請求消痛。 - 先安裝
npm install koa2-cors
; - 接著,在index.js中使用都哭。
const KoaCors = require('koa2-cors'); // ~~ 省略 ~~ // CORS跨域 app.use(KoaCors({ origin: ctx => { return ctx.request.header.origin; }, exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'], maxAge: 5, credentials: true, allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'], allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'Origin'], })); //本文由郝晨光整理總結(jié)并編寫肄满,未經(jīng)允許禁止轉(zhuǎn)載。
- 特別需要注意的是质涛,在
app.use()
使用koa2-cors
的時候稠歉,我們應(yīng)該把它放在別的中間件的前邊,特別是靜態(tài)資源處理和路由處理中間件的前邊汇陆,這樣可以保證我們在跨域請求靜態(tài)資源的時候不會出問題怒炸。
如果本文對您有幫助,可以看看本人的其他文章:
前端常見面試題(十三)@郝晨光
前端常見面試題(十二)@郝晨光
前端常見面試題(十一)@郝晨光