參考資料
環(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平臺(tái)的下一臺(tái)Web開(kāi)發(fā)框架,Koa是一個(gè)全新的Web框架,由Express原班人馬打造,致力于成為Web應(yīng)用和API開(kāi)發(fā)領(lǐng)域中更小且更加富有表現(xiàn)力、更健壯的基石。通過(guò)async
函數(shù)Koa幫你丟棄回調(diào)函數(shù),并有力地增強(qiáng)錯(cuò)誤處理。Koa并沒(méi)有綁定任何中間件,而是提供了一套優(yōu)雅的方法幫助快速編寫(xiě)服務(wù)器端應(yīng)用程序。
Koa2相比較Koa1最大區(qū)別在于中間件的寫(xiě)法,Koa1使用Generator,Koa2使用async/await
語(yǔ)法,因此Node.js必須大于v7.6.0+。
安裝
Koa依賴(lài)Node v7.6.0 或ES2015+ 和 async
方法支持。
創(chuàng)建工程文件夾并進(jìn)入,使用npm
初始化項(xiàng)目。
# 手工創(chuàng)建項(xià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" // 添加快捷啟動(dòng)命令
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
}
}
運(yùn)行腳本
可直接使用node app.js
運(yùn)行腳本,也可以使用它的快捷方式npm start
運(yùn)行app.js
腳本。
$ node app.js
$ npm start
安裝Koa
$ npm install koa -S
$ npm i koa
應(yīng)用程序
Koa應(yīng)用程序是一個(gè)包含一組中間件函數(shù)的對(duì)象,它按照類(lèi)似堆棧的方式組織和執(zhí)行。Koa類(lèi)似中間件系統(tǒng)。
$ vim app.js
導(dǎo)入Koa,在Koa2中由于導(dǎo)入的是一個(gè)類(lèi),所以使用大寫(xiě)的Koa。
const Koa = require("koa");
創(chuàng)建Koa對(duì)象,即Web應(yīng)用本身app
。
const app = new Koa();
對(duì)于任何HTTP請(qǐng)求,app將調(diào)用異步函數(shù)進(jìn)行處理。
app.use(async (ctx, next)=>{
await next();
// 設(shè)置HTTP響應(yīng)的內(nèi)容類(lèi)型
ctx.response.type = "text/html";
// 設(shè)置HTTP響應(yīng)的內(nèi)容
ctx.response.body = "hello world";
});
每當(dāng)Koa收到一個(gè)HTTP請(qǐng)求時(shí),會(huì)調(diào)用通過(guò)app.use()
注冊(cè)的async
異步函數(shù)并傳入ctx
和next
參數(shù)。可以針對(duì)ctx
進(jìn)行操作并設(shè)置返回內(nèi)容。
由async
標(biāo)記的函數(shù)稱(chēng)為異步函數(shù),使用await
調(diào)用另一個(gè)異步函數(shù),async/await
關(guān)鍵字是在ES7中引入的。
ctx
是Koa提供的Context對(duì)象,封裝了request
和response
對(duì)象,每次HTTP Request都會(huì)創(chuàng)建一個(gè)Context上下文對(duì)象。
ctx
是由Koa傳入的封裝了request
和response
的變量,可通過(guò)ctx.request
和ctx.response
訪(fǎng)問(wèn)請(qǐng)求和響應(yīng)。
中間件
next
是Koa傳入將要處理的下一個(gè)異步函數(shù),await next();
表示處理下一個(gè)異步函數(shù),然后再設(shè)置response
響應(yīng)的Content-Type
和body
內(nèi)容。
await next();
有什么作用呢?
Koa將多個(gè)async
異步函數(shù)組成了一個(gè)處理鏈,每個(gè)async
異步函數(shù)都可以做自己的事情,然后使用await next()
來(lái)調(diào)用下一個(gè)async
異步函數(shù)。這里將每個(gè)async
異步函數(shù)成為中間件middleware
,這些中間件可以組合起來(lái)完成很多實(shí)用的功能。
監(jiān)聽(tīng)3000端口
app.listen(3000);
瀏覽器訪(fǎng)問(wèn):http://127.0.0.1:3000
JS異步發(fā)展史
- ES5:回調(diào)函數(shù)
callback
- ES6:
Promise
對(duì)象和Generator
函數(shù) - ES7:
async/await
語(yǔ)法
async/await
async
用于聲明一個(gè)函數(shù)是異步的,await
只能出現(xiàn)在async
修改的函數(shù)中。async
函數(shù)會(huì)返回一個(gè)promise
對(duì)象,如果在async
函數(shù)返回一個(gè)直接量它最終將通過(guò)Promise.resolve
封裝成promise
對(duì)象,進(jìn)而可以直接通過(guò)promise
對(duì)象的then
方法來(lái)獲取這個(gè)直接量。
await
會(huì)暫停當(dāng)前async
的執(zhí)行,await
會(huì)阻塞代碼的執(zhí)行直到await
后的表達(dá)式處理完畢,代碼才繼續(xù)往下執(zhí)行。await
后的表達(dá)式可以是一個(gè)promise
對(duì)象也可以任何需要等待的值。如果await
等到的是一個(gè)promise
對(duì)象,await
就會(huì)阻塞后續(xù)代碼直到promise
對(duì)象resolve
后得到resolve
的值作為await
表達(dá)式的運(yùn)算結(jié)果。
async/await
只是一種語(yǔ)法糖,其代碼執(zhí)行與回調(diào)函數(shù)嵌套是本質(zhì)上是沒(méi)有區(qū)別的,只是寫(xiě)法上讓人以同步的思維去思考,避開(kāi)回調(diào)地獄。簡(jiǎn)而言之,async/await
是以同步的思維去編寫(xiě)異步的代碼,所以async/await
并不會(huì)影響Node.js的并發(fā)數(shù)。
中間件
中間件類(lèi)似于過(guò)濾器,用于在客戶(hù)端和應(yīng)用程序之間處理請(qǐng)求和響應(yīng)的方法。中間件的執(zhí)行類(lèi)似剝洋蔥,但并非一層層的執(zhí)行,而是以next
為分界,先執(zhí)行本層next
之前的部分,當(dāng)下一層中間件執(zhí)行完畢后再執(zhí)行本層next
之后的部分。
中間件是為了解決復(fù)雜應(yīng)用中頻繁回調(diào)而設(shè)計(jì)的級(jí)聯(lián)式代碼,它并不直接將控制權(quán)完全交給下一個(gè)中間件,而是碰到next
后去下一個(gè)中間件,等到下面都執(zhí)行完畢后在執(zhí)行next
后續(xù)代碼。
例如:使用三個(gè)中間件組成處理鏈,依次打印日志,記錄處理時(shí)間,輸出HTML。
//中間件處理鏈
app.use(async (ctx, next) => {
console.log(`${ctx.request.method} ${ctx.request.url}`);
await next();//調(diào)用下一個(gè)中間件
});
app.use(async (ctx, next) => {
const begin_time = new Date().getTime();
await next();//處理下一個(gè)中間件
const spend_ms = new Date().getTime() - begin_time;//耗費(fèi)時(shí)間
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í)行順序。如果某個(gè)中間件沒(méi)有調(diào)用await next()
則后續(xù)中間件將不再執(zhí)行。
例如:檢測(cè)用戶(hù)權(quán)限的中間件可以決定是否繼續(xù)處理請(qǐng)求,若不則直接返回403錯(cuò)誤。
app.use(async (ctx, next) => {
if(await checkAuth(ctx)){
await next();
}else{
ctx.response.status = 403;
}
});
簡(jiǎn)單來(lái)說(shuō),理解了中間件也就學(xué)會(huì)使用Koa了。
獲取GET請(qǐng)求數(shù)據(jù)
Koa2中GET請(qǐng)求可通過(guò)ctx.request
接收,接收方式分為兩種:
-
ctx.request.query
獲取格式化后的參數(shù)對(duì)象 -
ctx.request.querystring
獲取請(qǐng)求字符串 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請(qǐng)求數(shù)據(jù)
Koa2中沒(méi)有對(duì)POST請(qǐng)求的處理封裝獲取參數(shù)的方法,需要通過(guò)解析上下文ctx
中的元素來(lái)獲取,具體可分為三步:
- 解析上下文
ctx
對(duì)象中的原生Node.js對(duì)象req
- 將POST表單數(shù)據(jù)解析為
querystring
查詢(xún)字符串 - 將查詢(xún)字符串轉(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
中間件,對(duì)于POST請(qǐng)求的處理,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自動(dòng)重啟模塊
$ npm i supervisor -g
# 修改package.json,設(shè)置scripts選項(xiàng)下的start。
"scripts":
{
"test": "echo \"Error: no test specified\" && exit 1",
"start":"supervisor app.js" //設(shè)置npm start啟動(dòng)項(xiàng)
}
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的路由組件,路由的功能是指定具體的訪(fǎng)問(wèn)路徑,通過(guò)訪(fǎng)問(wèn)路徑指向特定的功能模塊。
安裝
# 安裝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實(shí)現(xiàn)靜態(tài)資源服務(wù)器
一個(gè)HTTP請(qǐng)求訪(fǎng)問(wèn)Web服務(wù)靜態(tài)資源,一般響應(yīng)結(jié)果有3種情況:
- 訪(fǎng)問(wèn)文本,如css、js、image...
- 訪(fǎng)問(wèn)靜態(tài)目錄
- 找不到資源則拋出404錯(cuò)誤
為Koa2實(shí)現(xiàn)靜態(tài)資源加載的服務(wù),用于訪(fǎng)問(wèn)css/js/img等靜態(tài)資源,若訪(fǎng)問(wèn)圖片則返回二進(jìn)制,其他文件則直接輸出字符串。
$ npm i koa-static -S
# 設(shè)置static目錄用于存儲(chǔ)靜態(tài)資源,app.js添加中間件。
app.use(require("koa-static")(__dirname + "/static"));
koa-views
koa-views
為Koa2加載的模板引擎
$ npm i koa-views -S
根目錄下創(chuàng)建views
目錄用于存儲(chǔ)視圖模板文件,并在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)境下對(duì)MySQL數(shù)據(jù)庫(kù)進(jìn)行CURD等操作。
$ npm i mysql -S