參考資料
環(huán)境部署
下載安裝NodeJS
下載地址: http://nodejs.cn/download/
$ node -v
v10.15.0
$ npm -v
6.4.1
Koa2
官網(wǎng):https://koa.bootcss.com/
Koa是基于Node.js平臺的下一臺Web開發(fā)框架看成,Koa是一個全新的Web框架嫌拣,由Express原班人馬打造,致力于成為Web應(yīng)用和API開發(fā)領(lǐng)域中更小且更加富有表現(xiàn)力蝗拿、更健壯的基石掷酗。通過async
函數(shù)Koa幫你丟棄回調(diào)函數(shù),并有力地增強錯誤處理。Koa并沒有綁定任何中間件漓穿,而是提供了一套優(yōu)雅的方法幫助快速編寫服務(wù)器端應(yīng)用程序。
Koa2相比較Koa1最大區(qū)別在于中間件的寫法注盈,Koa1使用Generator晃危,Koa2使用async/await
語法,因此Node.js必須大于v7.6.0+老客。
安裝
Koa依賴Node v7.6.0 或ES2015+ 和 async
方法支持僚饭。
創(chuàng)建工程文件夾并進入,使用npm
初始化項目胧砰。
# 手工創(chuàng)建項目
$ mkdir project && cd project
# 快速生成package.json文件
$ npm init -y
$ vim package.json
{
"name": "project",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start":"node app.js" // 添加快捷啟動命令
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
}
}
運行腳本
可直接使用node app.js
運行腳本鳍鸵,也可以使用它的快捷方式npm start
運行app.js
腳本。
$ node app.js
$ npm start
安裝Koa
$ npm install koa -S
$ npm i koa
應(yīng)用程序
Koa應(yīng)用程序是一個包含一組中間件函數(shù)的對象尉间,它按照類似堆棧的方式組織和執(zhí)行偿乖。Koa類似中間件系統(tǒng)。
$ vim app.js
導(dǎo)入Koa哲嘲,在Koa2中由于導(dǎo)入的是一個類贪薪,所以使用大寫的Koa。
const Koa = require("koa");
創(chuàng)建Koa對象眠副,即Web應(yīng)用本身app
画切。
const app = new Koa();
對于任何HTTP請求,app將調(diào)用異步函數(shù)進行處理囱怕。
app.use(async (ctx, next)=>{
await next();
// 設(shè)置HTTP響應(yīng)的內(nèi)容類型
ctx.response.type = "text/html";
// 設(shè)置HTTP響應(yīng)的內(nèi)容
ctx.response.body = "hello world";
});
每當(dāng)Koa收到一個HTTP請求時霍弹,會調(diào)用通過app.use()
注冊的async
異步函數(shù)并傳入ctx
和next
參數(shù)」馔浚可以針對ctx
進行操作并設(shè)置返回內(nèi)容庞萍。
由async
標(biāo)記的函數(shù)稱為異步函數(shù),使用await
調(diào)用另一個異步函數(shù)忘闻,async/await
關(guān)鍵字是在ES7中引入的钝计。
ctx
是Koa提供的Context對象,封裝了request
和response
對象,每次HTTP Request都會創(chuàng)建一個Context上下文對象私恬。
ctx
是由Koa傳入的封裝了request
和response
的變量债沮,可通過ctx.request
和ctx.response
訪問請求和響應(yīng)。
中間件
next
是Koa傳入將要處理的下一個異步函數(shù)本鸣,await next();
表示處理下一個異步函數(shù)疫衩,然后再設(shè)置response
響應(yīng)的Content-Type
和body
內(nèi)容。
await next();
有什么作用呢荣德?
Koa將多個async
異步函數(shù)組成了一個處理鏈闷煤,每個async
異步函數(shù)都可以做自己的事情,然后使用await next()
來調(diào)用下一個async
異步函數(shù)涮瞻。這里將每個async
異步函數(shù)成為中間件middleware
鲤拿,這些中間件可以組合起來完成很多實用的功能。
監(jiān)聽3000端口
app.listen(3000);
瀏覽器訪問:http://127.0.0.1:3000
JS異步發(fā)展史
- ES5:回調(diào)函數(shù)
callback
- ES6:
Promise
對象和Generator
函數(shù) - ES7:
async/await
語法
async/await
async
用于聲明一個函數(shù)是異步的署咽,await
只能出現(xiàn)在async
修改的函數(shù)中近顷。async
函數(shù)會返回一個promise
對象,如果在async
函數(shù)返回一個直接量它最終將通過Promise.resolve
封裝成promise
對象宁否,進而可以直接通過promise
對象的then
方法來獲取這個直接量窒升。
await
會暫停當(dāng)前async
的執(zhí)行,await
會阻塞代碼的執(zhí)行直到await
后的表達式處理完畢慕匠,代碼才繼續(xù)往下執(zhí)行饱须。await
后的表達式可以是一個promise
對象也可以任何需要等待的值。如果await
等到的是一個promise
對象絮重,await
就會阻塞后續(xù)代碼直到promise
對象resolve
后得到resolve
的值作為await
表達式的運算結(jié)果冤寿。
async/await
只是一種語法糖歹苦,其代碼執(zhí)行與回調(diào)函數(shù)嵌套是本質(zhì)上是沒有區(qū)別的青伤,只是寫法上讓人以同步的思維去思考,避開回調(diào)地獄殴瘦。簡而言之狠角,async/await
是以同步的思維去編寫異步的代碼,所以async/await
并不會影響Node.js的并發(fā)數(shù)蚪腋。
中間件
中間件類似于過濾器丰歌,用于在客戶端和應(yīng)用程序之間處理請求和響應(yīng)的方法。中間件的執(zhí)行類似剝洋蔥屉凯,但并非一層層的執(zhí)行立帖,而是以next
為分界,先執(zhí)行本層next
之前的部分悠砚,當(dāng)下一層中間件執(zhí)行完畢后再執(zhí)行本層next
之后的部分晓勇。
中間件是為了解決復(fù)雜應(yīng)用中頻繁回調(diào)而設(shè)計的級聯(lián)式代碼,它并不直接將控制權(quán)完全交給下一個中間件,而是碰到next
后去下一個中間件绑咱,等到下面都執(zhí)行完畢后在執(zhí)行next
后續(xù)代碼绰筛。
例如:使用三個中間件組成處理鏈,依次打印日志描融,記錄處理時間铝噩,輸出HTML。
//中間件處理鏈
app.use(async (ctx, next) => {
console.log(`${ctx.request.method} ${ctx.request.url}`);
await next();//調(diào)用下一個中間件
});
app.use(async (ctx, next) => {
const begin_time = new Date().getTime();
await next();//處理下一個中間件
const spend_ms = new Date().getTime() - begin_time;//耗費時間
console.log(`Time: ${spend_ms}ms`);
});
app.use(async (ctx, next) => {
await next();
ctx.response.type = "text/html";
ctx.response.body = "request received";
});
注意中間件的順序也就是調(diào)用app.use()
的順序?qū)Q定了中間件的執(zhí)行順序窿克。如果某個中間件沒有調(diào)用await next()
則后續(xù)中間件將不再執(zhí)行骏庸。
例如:檢測用戶權(quán)限的中間件可以決定是否繼續(xù)處理請求,若不則直接返回403錯誤年叮。
app.use(async (ctx, next) => {
if(await checkAuth(ctx)){
await next();
}else{
ctx.response.status = 403;
}
});
簡單來說敞恋,理解了中間件也就學(xué)會使用Koa了。
獲取GET請求數(shù)據(jù)
Koa2中GET請求可通過ctx.request
接收谋右,接收方式分為兩種:
-
ctx.request.query
獲取格式化后的參數(shù)對象 -
ctx.request.querystring
獲取請求字符串 ctx.query
ctx.querystring
例如:
app.use(async (ctx, next) => {
const url = ctx.url;
//使用ctx.request接收
const rq = ctx.request;
const rq_query = rq.query;
const rq_qs = rq.querystring;
//使用ctx接收
const ctx_query = ctx.query;
const ctx_qs = ctx.querystring;
//返回
body = {};
body.rq_query = rq_query;
body.rq_qs = rq_qs;
body.ctx_query = ctx_query;
body.ctx_qs = ctx_qs;
ctx.body = body;
});
獲取POST請求數(shù)據(jù)
Koa2中沒有對POST請求的處理封裝獲取參數(shù)的方法硬猫,需要通過解析上下文ctx
中的元素來獲取,具體可分為三步:
- 解析上下文
ctx
對象中的原生Node.js對象req
- 將POST表單數(shù)據(jù)解析為
querystring
查詢字符串 - 將查詢字符串轉(zhuǎn)換為JSON格式
const Koa = require("koa");
const app = new Koa();
const parseQueryString = (querystring) => {
const queryData = {};
const qsList = querystring.split("&");
for(let [index, qs] of qsList.entries()){
let kvList = qs.split("=");
queryData[kvList[0]] = decodeURIComponent(kvList[1]);
}
return queryData;
};
const parsePostData = (ctx) => {
return new Promise((resolve, reject) => {
try{
let postData = "";
ctx.req.on("data", (data)=>{
postData += data;
})
ctx.req.addListener("end", ()=>{
let parseData = parseQueryString(postData);
resolve(parseData);
});
}catch(error){
reject(error);
}
});
};
app.use(async(ctx, next) => {
if(ctx.method === "POST"){
let postData = await parsePostData(ctx);
ctx.body = postData;
}
});
app.listen(3000, ()=>{
console.log("server is running");
});
另一種方式是使用koa-bodyparser
中間件改执,對于POST請求的處理啸蜜,koa-bodyparser
中間件可以將Koa上下文中的formData
數(shù)據(jù)解析到ctx.request.body
中。
koa-bodyparser
安裝
$ npm i koa-bodyparser -S
使用
const Koa = require("koa");
const app = new Koa();
const bodyParser = require("koa-bodyparser");
app.use(bodyParser());
app.use(async(ctx, next) => {
if(ctx.method === "POST"){
ctx.body = ctx.request.body;
}
});
app.listen(3000, ()=>{
console.log("server is running");
});
supervisor
使用supervisor自動重啟模塊
$ npm i supervisor -g
# 修改package.json辈挂,設(shè)置scripts選項下的start衬横。
"scripts":
{
"test": "echo \"Error: no test specified\" && exit 1",
"start":"supervisor app.js" //設(shè)置npm start啟動項
}
koa-router
koa-router 路由組件
$ npm i koa-router -S
var Koa = require("koa");
var app = new Koa();
var router = require("koa-router")();
router.get("/", (ctx, next) => {
console.log("homepage");
});
app.use(router.routes()).use(router.allowedMethods());
參考資料
koa-router是Koa的路由組件,路由的功能是指定具體的訪問路徑终蒂,通過訪問路徑指向特定的功能模塊蜂林。
安裝
# 安裝Koa
$ npm install koa --save
# 安裝koa-router
$ npm install koa-router --save
使用
var Koa = require("koa");
var Router = require("koa-router");
var app = new Koa();
var router = new Router();
app.use(router.routes()).use(router.allowedMethods());
router.get("/", (ctx, next)=>{
});
koa-simple-router
路由koa-simple-router
$ npm i koa-simple-router -S
koa-cors 跨域組件
安裝
$ npm install --save koa2-cors
使用
var Koa = require('koa');
var cors = require('koa2-cors');
var app = new Koa();
app.use(cors());
koa-static
koa-static
為原生koa2實現(xiàn)靜態(tài)資源服務(wù)器
一個HTTP請求訪問Web服務(wù)靜態(tài)資源,一般響應(yīng)結(jié)果有3種情況:
- 訪問文本拇泣,如css噪叙、js、image...
- 訪問靜態(tài)目錄
- 找不到資源則拋出404錯誤
為Koa2實現(xiàn)靜態(tài)資源加載的服務(wù)霉翔,用于訪問css/js/img等靜態(tài)資源睁蕾,若訪問圖片則返回二進制,其他文件則直接輸出字符串债朵。
$ npm i koa-static -S
# 設(shè)置static目錄用于存儲靜態(tài)資源子眶,app.js添加中間件。
app.use(require("koa-static")(__dirname + "/static"));
koa-views
koa-views
為Koa2加載的模板引擎
$ npm i koa-views -S
根目錄下創(chuàng)建views
目錄用于存儲視圖模板文件序芦,并在app.js
文件中添加中間件配置臭杰。
//模板引擎
app.use(require("koa-views")(__dirname+"/views"));
koa-logger
//日志配置
app.use(require("koa-logger")());
koa-bodyparser
$ npm i koa-bodyparser@2 -S
app.use(require("koa-bodyparser")())
koa-session
$ npm i koa-session -S
mongoose
$ npm i mongoose -S
config
$ npm i config -S
mysql
mysql
模塊是node操作MySQL的引擎,可在node.js環(huán)境下對MySQL數(shù)據(jù)庫進行CURD等操作谚中。
$ npm i mysql -S