習(xí)慣NodeJs
? ? ? ? ? Node.js是一個基于Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境。V8 是 Google 發(fā)布的開源 JavaScript 引擎 ,本身就是用于 Chrome 瀏覽器的 JS 解釋部分,但是 Ryan Dahl 這哥們,鬼才般的,把這個 V8 搬到了服務(wù)器上,用于做服務(wù)器的軟件。
?優(yōu)勢
? ? ? ? ?Node.js使用了一個事件驅(qū)動、非阻塞式 I/O 的模型,使其輕量又高效?;诖薔odeJs具有超強(qiáng)的高并發(fā)能力,因此,可以實(shí)現(xiàn)實(shí)現(xiàn)高性能服務(wù)器。
? ? ? ? ?另外,NodeJs 語法完全是js語法,只要你懂js基礎(chǔ)就可以學(xué)會Nodejs后端開發(fā)。Node 打破了過去 JavaScript 只能在瀏覽器中運(yùn)行的局面。前后端編程環(huán)境統(tǒng)一,可以大大降低開發(fā)成本。? ? ? ? ?
? ? ? ?在官網(wǎng)下載nodeJs(http://nodejs.cn/),最新版本為v10.13.0,建議node版本高于v7.6,因?yàn)楹竺嫖覀儠玫降膋oa2,要求node版本大于v7.6?。
? ? ??更加推薦的方法是使用NVM來管理node版本。具體使用見藍(lán)景電子書(http://html5book.bluej.cn),搜索“nvm”即可。
helloWorld
//helloWorld.js
console.log(“hello World!”)
//打開命令行并進(jìn)入項(xiàng)目文件夾
node helloWorld.js
是不是非常簡單!
開啟一個服務(wù)器
我們在使用apach或者nginx,他們本身能提供一個服務(wù)器功能,用來處理請求.但在nodejs中,這個服務(wù)器需要我們自己來搭建,創(chuàng)建好服務(wù)器以后記得在瀏覽器里面訪問他一下額.
//不支持import
const http = require(“http”);
let httpServer = http.createServer((request,response)=>{
console.log(“有訪問來了”);
console.log(request.url);
console.log(request.headers);
console.log(request.method);
response.writeHead(200,“ok”,{“Content-Type”:“text/html;charset=UTF-8”});
response.write(“孩子瀏覽器已經(jīng)接收到了你的請求”);
response.end();
});
//這里的host可以寫成其他的"127.0.1.1"
httpServer.listen(3000,“l(fā)ocalhost”);
上面代碼是一個koa項(xiàng)目的基本組成結(jié)構(gòu),如果執(zhí)行兩次的話,是因?yàn)闉g覽器主動請求了"/favicon.icon"一次.下面我們看幾個要注意的點(diǎn):
app.listen(…) 只是一個語法糖
等價于下面的寫法
//app.listen(…) 方法只是以下方法的語法糖:
const http = require(‘http’);
const Koa = require(‘koa’);
const app = new Koa();
http.createServer(app.callback()).listen(3000);
這意味著您可以將同一個應(yīng)用程序同時作為 HTTP 和 HTTPS 或多個地址:
const http = require(‘http’);
const https = require(‘https’);
const Koa = require(‘koa’);
const app = new Koa();
http.createServer(app.callback()).listen(3000);
https.createServer(app.callback()).listen(3001);
app.use() 到底是個什么鬼,怎么用?
首先,koa開啟的web服務(wù),每收到一個http請求,koa就會調(diào)用通過app.use()注冊的async函數(shù)(當(dāng)然也可以是普通函數(shù)),并傳入ctx和next參數(shù).這就是我們經(jīng)常說的"中間件"了.
其次,可以多次調(diào)用use方法來注冊中間件.
最后,use注冊的async函數(shù)里面的next函數(shù)意義重大.當(dāng)執(zhí)行next()函數(shù)時,會將程序控制權(quán)(例如修改ctx.response.body)交給下一個中間件,下一個中間件再次調(diào)用next()函數(shù),依次走下去.如果中間有一個中間件沒有調(diào)用next()函數(shù),那么后續(xù)的中間件就不會執(zhí)行,當(dāng)然最后中間件可以不執(zhí)行next函數(shù).
洋蔥模型: 這里指的是next在中間件中調(diào)用的位置不同,代碼執(zhí)行的順序也會不同.當(dāng)中間件拿到控制權(quán)后,就開始執(zhí)行use注冊的async函數(shù),當(dāng)前async函數(shù)里面的代碼,如果在next()調(diào)用前面會馬上執(zhí)行,如果代碼在next()后面的話,就不要意思了,只能等下一個中間件執(zhí)行完畢后再回來執(zhí)行你了.簡單點(diǎn)理解:next()就是執(zhí)行下一個中間件,并且造成"阻塞".結(jié)合我們上述app.js的示例,你的網(wǎng)頁會先輸出"Bgg神教,一統(tǒng)江湖",后輸出"hello,koa2",就是這個道理!
插播一條消息,你發(fā)現(xiàn)我們的示例ctx.response.body += "<h1>hello,koa2</h1>";是用的+=,說明多個組件其實(shí)控制的是同一個response.
async函數(shù)里面的ctx
ctx作為上下文使用,包括基本的ctx.request和ctx.response.為方便起見許多上下文的訪問器和方法直接委托給它們的 ctx.request 或 ctx.response,不然的話它們是相同的. 例如 ctx.type 和 ctx.length 委托給 response 對象,ctx.path 和 ctx.method 委托給 request.
ctx.request/ctx.response 是 Koa 的 request/response 對象. 而ctx.req/ctx.res 是 node 的 request/response 對象,奇特吧!不過繞過 Koa 的 response 處理是不被支持的,所以避免使用ctx.res.write()等原生 response 操作.
除此之外,koa還約定了一個中間件的存儲空間 ctx.state ,通過這個 state 可以儲存一些的數(shù)據(jù),比如用戶數(shù)據(jù),另外類似 koa-views 這些渲染view層的中間件也會默認(rèn)把 ctx.state 里面的屬性作為 view 的上下文傳入.如果使用webpack打包的話可以使用中間將加載資源的方法作為 ctx.state 的屬性傳入到view層使之獲取資源路徑.
拓展知識點(diǎn)
app.context
app.context 是從其創(chuàng)建 ctx 的原型。您可以通過編輯 app.context 為 ctx 添加其他屬性。這對于將 ctx 添加到整個應(yīng)用程序中使用的屬性或方法非常有用,這可能會更加有效(不需要中間件)和/或 更簡單(更少的 require()),而更多地依賴于ctx,這可以被認(rèn)為是一種反模式。
例如,要從 ctx 添加對數(shù)據(jù)庫的引用:
app.context.db = db();
app.use(async ctx => {
console.log(ctx.db);
});
錯誤處理
默認(rèn)情況下,將所有錯誤輸出到 stderr,除非 app.silent 為 true。 當(dāng) err.status 是 404 或 err.expose 是 true 時默認(rèn)錯誤處理程序也不會輸出錯誤。 要執(zhí)行自定義錯誤處理邏輯,如集中式日志記錄,您可以添加一個 “error” 事件偵聽器:
app.on(‘error’, err => {
log.error(‘server error’, err)
});
//如果 req/res 期間出現(xiàn)錯誤,并且 無法 響應(yīng)客戶端,Context實(shí)例仍然被傳遞:
app.on(‘error’, (err, ctx) => {
log.error(‘server error’, err, ctx)
});
Request 別名
ctx.header
ctx.headers
ctx.method
ctx.method=
ctx.url
ctx.url=
ctx.originalUrl
ctx.origin
ctx.href
ctx.path
ctx.path=
ctx.query
ctx.query=
ctx.querystring
ctx.querystring=
ctx.host
ctx.hostname
ctx.fresh
ctx.stale
ctx.socket
ctx.protocol
ctx.secure
ctx.ip
ctx.ips
ctx.subdomains
ctx.is()
ctx.accepts()
ctx.acceptsEncodings()
ctx.acceptsCharsets()
ctx.acceptsLanguages()
ctx.get()
Response 別名
ctx.body
ctx.body=
ctx.status
ctx.status=
ctx.message
ctx.message=
ctx.length=
ctx.length
ctx.type=
ctx.type
ctx.headerSent
ctx.redirect()
ctx.attachment()
ctx.set()
ctx.append()
ctx.remove()
ctx.lastModified=
ctx.etag=