前言
跟好朋友打賭些阅,我要來(lái)個(gè)技術(shù)文章日更。于是斑唬,我跑到到群里喊了句扑眉,我要日更。沒(méi)想到赖钞,得到的是大家的支持與鼓勵(lì)腰素。感謝小伙伴們,逼著我上了梁山......
此文雪营,就是我日更文章的開始弓千,不知道自己能堅(jiān)持多久。最開始打算將文章標(biāo)題名定為《圍繞node60天》献起,一個(gè)叫老張的網(wǎng)友看了一下洋访,隨口說(shuō)了句短小搓...呵呵呵...短小搓是什么鬼?于是谴餐,我給文章起了個(gè)高大上的名字姻政,《諾的那些事》......這次夠高大上了吧,哈哈哈哈岂嗓。本來(lái)想給淺談我眼中的express汁展、koa和koa2定為副標(biāo)題的,可恨簡(jiǎn)書本著回歸寫作根本的理念,不存在副標(biāo)題這個(gè)功能食绿,飲恨了侈咕。
我是個(gè)node程序開發(fā)者。此前器紧,我一直認(rèn)為自己是一個(gè)堅(jiān)定的java信徒耀销,我感覺(jué)java才是最終改變世界的語(yǔ)言,甚至我出國(guó)旅游去的都是java island......在用了7年Java之后铲汪,我遇見了node熊尉。遇見node的那一瞬間,我好似看見了蜂腰肥臀的妙齡少女掌腰,一群群一片片的在我面前歡歌笑舞帽揪,我知道,是時(shí)候放棄java了辅斟,java就這樣漸漸成了往事转晰。不過(guò),我跟java肯定還是藕斷絲連的士飒,畢竟hadoop這樣的程序還是需要用java來(lái)寫的查邢,另外,7年的武功是說(shuō)什么也忘不了了酵幕。到現(xiàn)在為止扰藕,我用了3年node,見證了node的成長(zhǎng)芳撒,也見證了自己的成長(zhǎng)邓深。我準(zhǔn)備好好說(shuō)說(shuō)node的那些事,跟大家分享笔刹,接受大家的意見和建議芥备。
跟很多人一樣,我是以express開啟自己的node之旅的舌菜。express是什么呢萌壳?它是一個(gè)封裝了Connect的、并提供web服務(wù)的中間件日月,是開發(fā)web程序的利器袱瓮。express是由TJ大神開發(fā)的,之后爱咬,TJ大神又開發(fā)了koa這款神器尺借。但是,因?yàn)榫猓琫s7的飛速發(fā)展燎斩,koa又迅速衍化出了koa2這個(gè)版本虱歪,時(shí)至今日,koa的github已經(jīng)全面更換為了koa2版本的代碼瘫里,當(dāng)然实蔽,這一切荡碾,還真就是在我眼前發(fā)生的谨读,作為歷史的見證者,我要談?wù)勎覍?duì)這幾個(gè)框架的看法坛吁。
說(shuō)個(gè)題外話劳殖,我的文章,不打算走傳統(tǒng)技術(shù)文章的套路拨脉,我講就講一些不一樣的哆姻,講一些平常你看不到的東西,希望各位技術(shù)大牛多多給小弟指點(diǎn)玫膀,在下這廂有禮了矛缨。
酷事
單田芳老爺子,有幾部超級(jí)好聽的長(zhǎng)篇評(píng)述帖旨,叫三俠五義箕昭,其中,有個(gè)重要的人物解阅,白眉大俠徐良(刀是什么樣的刀落竹,金絲大環(huán)刀,劍是什么樣的劍货抄,閉月羞光劍......)述召,白眉大俠徐良是一個(gè)早產(chǎn)兒,從小體弱多病蟹地,但是最終憑借著刻苦努力积暖,成為了一代武術(shù)宗師。node的經(jīng)歷跟白眉大俠很像怪与,node也是個(gè)早產(chǎn)兒呀酸。早期的node問(wèn)題多多,那個(gè)時(shí)候琼梆,在win上安裝node簡(jiǎn)直就是一件災(zāi)難性誉。不過(guò),隨著時(shí)間的推移茎杂,node變得越來(lái)越好用错览,node的好基友npm也變得越來(lái)越龐大。我從node4.0開始入門煌往,后來(lái)經(jīng)歷了node6.0版本的各種無(wú)奈和妥協(xié)倾哺,緊接著就迎來(lái)了node7.6版本的絕地反擊轧邪,直到現(xiàn)在,感受到了node7.9的大徹大悟羞海。
記得當(dāng)初和一個(gè)技術(shù)猿聊天忌愚,他跟我抱怨,node里全是大坑却邓。我一聽硕糊,這位仁兄用的是express吧,這位仁兄告訴我腊徙,沒(méi)有简十,他們自己實(shí)現(xiàn)了個(gè)輪子,跟express差不多撬腾,現(xiàn)在正在考慮著升級(jí)的事宜呢螟蝙。呵呵呵了,造了個(gè)輪子民傻,還跟express差不多胰默。不過(guò),這也從另外一個(gè)側(cè)面看出了node當(dāng)年不可回避的幾個(gè)問(wèn)題:
1.node早期就是個(gè)極客玩具漓踢,高手眾多牵署,產(chǎn)品野蠻生長(zhǎng)。
2.使用node開發(fā)商業(yè)軟件是一件正在發(fā)生的事情彭雾。(對(duì)了碟刺,阿里、騰訊薯酝、非死不可半沽、領(lǐng)英這些大佬也都用node重構(gòu)了一些適合的模塊。)
3.那個(gè)時(shí)候吴菠,使用node的同志們不是傻者填,就是用雞湯晃點(diǎn)了他們老板。
回調(diào)大坑
不過(guò)這個(gè)仁兄倒是也說(shuō)出了node的一些痛點(diǎn)做葵,其中占哟,node存在的技術(shù)大坑可能大家都遇到過(guò),這些坑是因?yàn)閚ode的語(yǔ)言特性導(dǎo)致的酿矢。例如榨乎,回調(diào)大坑,就是這些坑中瘫筐,最深的一個(gè)蜜暑。
下邊是一個(gè)回調(diào)大坑的代碼示例,我們來(lái)感受一下:
module.exports = function (param, cb) {
asyncFun1(param, function (er, data) {
if (er) return cb(er);
asyncFun2(data,function (er,data) {
if (er) return cb(er);
asyncFun3(data, function (er, data) {
if (er) return cb(er);
cb(data);
})
})
})
}
node本身是異步回調(diào)的策肝,通過(guò)高階函數(shù)肛捍,偏函數(shù)實(shí)現(xiàn)回調(diào)函數(shù)隐绵。但是,回調(diào)函數(shù)嵌套過(guò)多拙毫,會(huì)使代碼不可閱讀依许、不可描述,那位仁兄說(shuō)的大坑便是callback hell缀蹄,翻譯過(guò)來(lái)就是回調(diào)大坑峭跳,或者說(shuō)是Pyramid of Doom,邪惡金字塔袍患。(我樸素的認(rèn)為這位仁兄只遇到了這一個(gè)大坑.....呵呵呵)
回調(diào)大坑怎么解決呢坦康?es5可以利用一下第三方庫(kù)竣付,例如async庫(kù)诡延,或者單純使用connect中間件提供的next功能來(lái)處理,還可以利用promise來(lái)處理回調(diào)大坑古胆。當(dāng)然肆良,單純使用promise可能給自己帶來(lái)另外一個(gè)大坑,then大坑逸绎,或者叫pipe大坑惹恃,無(wú)數(shù)個(gè)then,想想也是夠恐怖的棺牧。另外巫糙,還可以使用node自帶的事件模塊來(lái)處理回調(diào)問(wèn)題,利用事件代理(我記得是backbone的一個(gè)模塊)來(lái)簡(jiǎn)化代碼書寫颊乘。關(guān)于事件模塊参淹,我之后會(huì)寫個(gè)小專題,來(lái)說(shuō)說(shuō)node的事件原理乏悄。不過(guò)浙值,雖然提到了事件模塊,但是檩小,我不推薦用事件去處理回調(diào)嵌套开呐,因?yàn)椋枰獙懜嗟拇a规求,得不償失筐付。
這里說(shuō)個(gè)題外話,在樸大人(樸靈)的一篇文章中阻肿,提到了wind庫(kù)和step庫(kù)瓦戚,此處就不進(jìn)行介紹了,因?yàn)槊崦琫s6和es7會(huì)給我們更加好的使用體驗(yàn)伤极。
es5講完了蛹找,各種基于es5來(lái)處理回調(diào)的方法也講完了。其實(shí)哨坪,我在使用這些方法來(lái)簡(jiǎn)化回調(diào)嵌套的時(shí)候庸疾,總感覺(jué)是脫了褲子放屁,講真当编,有的時(shí)候届慈,不僅沒(méi)有簡(jiǎn)化代碼,還會(huì)造成其他的代碼閱讀障礙忿偷,增加團(tuán)隊(duì)的學(xué)習(xí)成本金顿。歸根結(jié)底,我們的代碼是寫給人看的鲤桥,機(jī)器只是順便執(zhí)行一下而已揍拆。
ES的官方組織,肯定認(rèn)識(shí)到了這一點(diǎn)茶凳,于是嫂拴,基于協(xié)程原理的規(guī)范也呼之欲出,終于在ES6中為大家?guī)?lái)了Generator函數(shù)贮喧。
Coroutine筒狠,協(xié)程,簡(jiǎn)單來(lái)說(shuō)就是由用戶通過(guò)特定的程序語(yǔ)言控制CPU切換和掛起進(jìn)程(Process)或者線程(Thread)箱沦,用同步的方式來(lái)模擬異步程序辩恼,我之后會(huì)單獨(dú)講一下進(jìn)程、線程谓形、協(xié)程灶伊,在此不做過(guò)多展開。既然提供了協(xié)程的功能套耕,那么我們處理起回調(diào)也就迎刃而解了谁帕。
Generator函數(shù)和yield語(yǔ)句是一對(duì)好基友,如果沒(méi)有yield語(yǔ)句的話冯袍,Generator函數(shù)只不過(guò)是暫緩執(zhí)行的狀態(tài)機(jī)而已匈挖。通過(guò)配合yield,Generator 函數(shù)就可以暫停執(zhí)行和恢復(fù)執(zhí)行康愤,從而將其內(nèi)部封裝的異步函數(shù)變?yōu)橥綀?zhí)行儡循。下面我們看看例子來(lái)感受一下:
function* gen(x){
var y = yield x + 2;
return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next(2) // { value: 2, done: true }
當(dāng)然Generator函數(shù)還有些濫用之嫌,具體為什么征冷,我會(huì)在后續(xù)的文章中做出解釋择膝。反正,ES官方組織他們對(duì)于Generator函數(shù)是不滿意的检激。于是肴捉,ES官方組織馬不停蹄腹侣,終于在ES7規(guī)范中,搗鼓出來(lái)了async/await這個(gè)目前為止齿穗,異步回調(diào)最佳的解決方案傲隶。
本質(zhì)上講,async/await規(guī)范是Generator函數(shù)+yield語(yǔ)句的語(yǔ)法糖窃页。返回部分都是一個(gè)Promise對(duì)象跺株。async/await規(guī)范比Generator函數(shù)+yield語(yǔ)句要更加好用,下面我們看看例子來(lái)感受一下:
var sleep = function (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, time);
})
};
var go= async function () {
console.log('start');
await sleep(3000);
console.log('end');
};
go();
至此脖卖,回調(diào)大坑的問(wèn)題算是得到了很好的解決乒省,我們接下來(lái),就說(shuō)是express畦木、koa和koa2這三個(gè)框架袖扛。
TJ大神的三大英雄
每每看到TJ大神的頭像,我都會(huì)想起一個(gè)詞:preconception馋劈。偏見攻锰,先入之見晾嘶。許多人妓雾,對(duì)于程序員是存在很大的偏見的。大多數(shù)人的腦海中給程序員的定義都是這個(gè)樣子:
一般人認(rèn)為程序員:不夠健康械姻,不夠整潔,不夠潮流机断,人傻楷拳,錢多......TJ大神,這個(gè)頂級(jí)程序員吏奸,則給我們上了非常生動(dòng)的一課欢揖。TJ是設(shè)計(jì)師出身,半路出家做了程序員奋蔚,他一個(gè)人完成了express她混、koa、koa2設(shè)計(jì)和核心開發(fā)泊碑。TJ曾經(jīng)說(shuō)過(guò)坤按,他之所以能做出這些NB的軟件,是因?yàn)槁麩釔坶喿x其他大牛的源碼臭脓,他會(huì)把自己不明白的問(wèn)題都弄懂。他在第一時(shí)間遇見了問(wèn)題腹忽,處理了問(wèn)題来累,保證自己深刻理解各種軟件的核心原理和運(yùn)行機(jī)制砚作。于是,TJ就變成了一個(gè)npm包貢獻(xiàn)極多的node大神嘹锁,他的光輝和事跡最終會(huì)變?yōu)閭髡f(shuō)偎巢,再node圈里永久的流傳下去......
三大英雄
node的早期,是荒蕪的年代兼耀,正如之前我的那個(gè)哥們一樣压昼,那個(gè)時(shí)候,沒(méi)有輪子瘤运。程序員自己制造了各種各樣的輪子窍霞,真可謂是八仙過(guò)海,各顯神通拯坟。那個(gè)時(shí)候node程序員一般這樣開始寫web應(yīng)用:
var http = require('http');
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World\n');
}).listen(8888);
console.log('Server running at http://127.0.0.1:8888/');
終于但金,TJ打造了express、koa郁季、koa2三大英雄冷溃,node的浪漫主義年代逐漸揭開了序幕。我們看下邊的表:
英雄 | 說(shuō)明 | 對(duì)應(yīng) | 經(jīng)典 |
---|---|---|---|
express | web框架 | es5 | 回調(diào)嵌套 |
koa | web框架 | es6 | Generator函數(shù)+yield語(yǔ)句+Promise |
koa2 | web框架 | es7 | async/await+Promise |
下面我就開始說(shuō)一下這三個(gè)框架和他們之間千絲萬(wàn)縷的聯(lián)系
初代英雄:express
express的入門非常簡(jiǎn)單梦裂,通過(guò)創(chuàng)建express的Application就構(gòu)建了一個(gè)expressweb實(shí)例似枕。下面我們看看例子來(lái)感受一下:
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
express本身封裝了路由模塊,因此年柠,可以利用express直接處理各種http路由請(qǐng)求凿歼。
在express用四個(gè)主要模塊:
模塊 | 說(shuō)明 | 解釋 |
---|---|---|
Application | web服務(wù)器模塊 |
|
Request | 請(qǐng)求 | |
Response | 響應(yīng) | |
Router | 路由 |
express用Application掀抹、Request虐拓、Response、Router四個(gè)主要模塊傲武,模擬了一個(gè)完整的web服務(wù)器功能蓉驹,對(duì)了,express還在相當(dāng)長(zhǎng)的一段時(shí)期中受到了Connect的影響谱轨。在使用express的過(guò)程中戒幔,你會(huì)發(fā)現(xiàn)express是一個(gè)極簡(jiǎn)的、靈活的 web 應(yīng)用開發(fā)框架土童,它提供的這一系列強(qiáng)大的特性诗茎,可以幫助你快速創(chuàng)建各種 web 和移動(dòng)設(shè)備應(yīng)用。
二代英雄:koa
目前的koa官方github已經(jīng)全面的使用koa2版本的代碼了,換句話說(shuō)敢订,koa和koa2現(xiàn)在只是版本上的區(qū)別了王污,koa是老版本,koa2用新的版本號(hào)楚午。因此昭齐,koa1,我們需要查看老的代碼版本矾柜。
koa 是由 express原班人馬打造的(TJ)阱驾,致力于構(gòu)建更小、更富有表現(xiàn)力怪蔑、更健壯的 web 框架里覆。使用 koa 編寫 web 應(yīng)用,通過(guò)組合不同的 generator缆瓣,可以免除重復(fù)繁瑣的回調(diào)函數(shù)嵌套喧枷,并極大地提升錯(cuò)誤處理的效率。koa 不在內(nèi)核方法中綁定任何中間件弓坞,它僅僅提供了一個(gè)輕量?jī)?yōu)雅的函數(shù)庫(kù)隧甚,使得編寫 koa 應(yīng)用變得得心應(yīng)手。
Koa 包含了像 content-negotiation(內(nèi)容協(xié)商)渡冻、cache freshness(緩存刷新)戚扳、proxy support(代理支持)和 redirection(重定向)等常用任務(wù)方法。 與提供龐大的函數(shù)支持不同菩帝,Koa只包含很小的一部分咖城,因?yàn)镵oa并不綁定任何中間件。
koa中也包含4個(gè)主要模塊呼奢,Application、Request切平、Response握础、Context。此時(shí)悴品,router已經(jīng)被排除在內(nèi)核之外了禀综。其實(shí),koa只是一個(gè)“中間架”苔严,幾乎所有的功能都需要由第三方中間件來(lái)協(xié)同完成定枷。例如koa的router模塊,就有20多個(gè)届氢,優(yōu)勝劣汰欠窒,自由選擇......雖然有不規(guī)范之嫌,但是退子,koa是規(guī)范的這就足夠了岖妄。使用koa型将,可以最大限度的發(fā)揮自己的想象力,利用koa荐虐,構(gòu)建各種個(gè)性化的web與移動(dòng)應(yīng)用七兜。下面我們看看例子來(lái)感受一下:
var koa = require('koa');
var app = koa();
app.use(function *(){
this.body = 'Hello World';
});
app.listen(3000);
沒(méi)錯(cuò),就是這么簡(jiǎn)單福扬,使用了Generator函數(shù)腕铸,這也是koa和express最大的不同,express是回調(diào)函數(shù)铛碑,koa是用Generator來(lái)作為響應(yīng)器的恬惯。
另外,那個(gè)替代了router的context是怎樣的呢亚茬?下面我們看看例子來(lái)感受一下:
app.use(function *(){
this; // is the Context
this.request; // is a koa Request
this.response; // is a koa Response
});
另外酪耳,koa中還有co這個(gè)工具。co是一個(gè)“皮”刹缝,通過(guò)co來(lái)包裝Generator和yeild碗暗,下面我們看看例子來(lái)感受一下:
var co = require('co');
co(function *(){
// yield any promise
var result = yield Promise.resolve(true);
}).catch(onerror);
co(function *(){
// resolve multiple promises in parallel
var a = Promise.resolve(1);
var b = Promise.resolve(2);
var c = Promise.resolve(3);
var res = yield [a, b, c];
console.log(res);
// => [1, 2, 3]
}).catch(onerror);
// errors can be try/catched
co(function *(){
try {
yield Promise.reject(new Error('boom'));
} catch (err) {
console.error(err.message); // "boom"
}
}).catch(onerror);
function onerror(err) {
// log any uncaught errors
// co will not throw any errors you do not handle!!!
// HANDLE ALL YOUR ERRORS!!!
console.error(err.stack);
}
雖然,前邊說(shuō)了很多express梢夯、koa的相關(guān)知識(shí)言疗,但是,這兩個(gè)都不重要了颂砸,隨著koa2的扶正和node 7.6的發(fā)布噪奄,基于nodejs的程序開發(fā),開啟了新的篇章人乓。
三代英雄:koa2
上一節(jié)已經(jīng)提到勤篮,目前的koa官方github已經(jīng)全面的使用koa2版本的代碼了,并且有一句非常重要的提示Koa requires node v7.6.0 or higher for ES2015 and async function support.
色罚。意思是說(shuō)碰缔,koa需要至少node v7.6.0版本和ES2015(es6+async)才能使用。這個(gè)提示戳护,也是非常重要的一句話金抡,從這個(gè)版本開始,我們可以拋棄Bable(當(dāng)然腌且,nodev7.6還是不能完全拋棄babel梗肝,因?yàn)榈侥壳盀橹梗琻ode都還沒(méi)有實(shí)現(xiàn)對(duì)import和export的支持铺董,感謝深藍(lán)wbe的提醒)巫击,快樂(lè)的使用async等新的語(yǔ)法了。(Babel 自帶了一組 ES2015 語(yǔ)法轉(zhuǎn)化器。這些轉(zhuǎn)化器能讓你現(xiàn)在就使用最新的 JavaScript 語(yǔ)法喘鸟,而不用等待瀏覽器和node提供支持匆绣。)
目前,koa2結(jié)合了async/await已經(jīng)成為了最好的web開發(fā)框架什黑。上一節(jié)崎淳,已經(jīng)講了koa的主要模塊和實(shí)現(xiàn)原理,此處愕把,我只是簡(jiǎn)單說(shuō)說(shuō)koa2和koa不同之處拣凹,下面我們看看例子來(lái)感受一下:
const Koa = require('koa');
const app = new Koa();
app.use(ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
函數(shù)式編程,async/await功能恨豁,程序簡(jiǎn)單嚣镜,好用,真可謂是居家旅行的不二之選呀橘蜜。通過(guò)查看代碼淑履,koa2去除了co中間件形用,進(jìn)一步的精簡(jiǎn)了內(nèi)核芹务,這一點(diǎn)也正好符合當(dāng)下性冷淡風(fēng)格的設(shè)計(jì)潮流......不禁想贊嘆一句薄湿,TJ不愧是設(shè)計(jì)師出身呀......
小結(jié)
其實(shí),很多人不關(guān)心框架的原理象颖,他們關(guān)心的是如何從express升級(jí)到Koa佩厚,如何從koa升級(jí)到koa2。雖然koa
對(duì)于向前兼容的并不好说订,但是抄瓦,我在這里想提出另外一種思路,就是利用express+async/await的形式來(lái)做升級(jí)陶冷。一方面钙姊,可以在不改變?cè)谐绦虻幕A(chǔ)上使用最新的語(yǔ)法特性,另一方面埃叭,可以用最小的代價(jià)獲取最大的效益摸恍。下面我們看看例子來(lái)感受一下:
var PageQuery = async (page, pageSize, Model, populate, queryParams, sortParams) => {
var start = (page - 1) * pageSize;
var $page = {
pageNumber: page
};
let TotleRow = await ModelCount(Model, queryParams);
let records = await PageRecords(Model, queryParams, start, pageSize, populate, sortParams);
$page.TotleRow = TotleRow;//(count - 1) / pageSize + 1;
$page.PageCount=parseInt(TotleRow/pageSize)+1;
$page.results = records;
return $page;
};
var ModelCount = (Model, queryParams) => {
return Model.count(queryParams).exec().then((count) => {
return count;
}).catch((err) => {
console.log("err:" + err);
return err;
});
}
var PageRecords = (Model, queryParams, start, pageSize, populate, sortParams) => {
return Model
.find(queryParams)
.skip(start)
.limit(pageSize)
.populate(populate)
.sort(sortParams)
.exec()
.then((doc) => {
return doc;
}).catch((err) => {
console.log("err:" + err);
return err;
});
}
//......
Customer.PageQuery(pageNum, pageSize, Customer.Model, "", {}, {}).then((pageResult) => {
res.render('customersList', {
layout: "admin",
customersList: pageResult.results,
totalPages: pageResult.PageCount,
pno: pageNum,
});
}).catch((err) => {
console.log("err:" + err);
res.send("err");
});
上邊是我寫的一個(gè)中間件,可以通過(guò)這種簡(jiǎn)單的方式赤屋,來(lái)處理express與es7的升級(jí)問(wèn)題。(完整代碼可以到我的github上去查看https://github.com/lxlhum/meet_quick 壁袄,大家?guī)臀野研切屈c(diǎn)起來(lái)哈)
尾聲
流行的web技術(shù)統(tǒng)計(jì):node类早、ruby、python嗜逻、php涩僻、java
現(xiàn)在,node的社區(qū)非常活躍逆日,產(chǎn)品換代升級(jí)非常迅速和及時(shí)嵌巷。使用node,既要求我們用扎實(shí)的功底室抽,又要求我們與時(shí)俱進(jìn)搪哪,不斷學(xué)習(xí)。畢竟坪圾,不管用什么語(yǔ)言晓折,在程序開發(fā)的道路上,只有不斷學(xué)習(xí)兽泄,才能不斷前進(jìn)漓概。
這部分關(guān)于node的基礎(chǔ)知識(shí),可以看我寫的一個(gè)筆記:深入淺出NodeJS的讀書筆記病梢,保證您讀過(guò)以后胃珍,會(huì)有豁然開朗之感。筆記中蜓陌,記錄了tcp/ip觅彰,http,socket/websocket的相關(guān)知識(shí)护奈,全方位的介紹nodejs缔莲。