Node.js自學完全總結

2017年3月攝于圓明園

零穴店、什么是Node.js?

引用Node.js官方網站的解釋如下:

Node.js? is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

翻譯成中文就是:

Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行環(huán)境途茫。
Node.js 使用了一個事件驅動非阻塞式 I/O 的模型簿晓,使其輕量又高效症革。

1、 運行環(huán)境(Runtime)

如果做一個類比仔掸,Node.js與JavaScript關系脆贵,就像JDK(Java Development Kit)與Java的關系。
總的來說起暮,Node.js不是一門語言卖氨,而是用來進行Web開發(fā)的Runtime。

2、事件驅動(Event-driven)

在前端web開發(fā)中比較常見的事件驅動例子是筒捺,給一個按鈕綁定一個事件處理程序柏腻,這個事件處理程序就是事件驅動的,JavaScript進程并不知道什么時候調用它系吭,點擊按鈕五嫂,觸發(fā)Click事件,此時主程序得到相應的通知肯尺,就知道調用綁定的的事件處理程序了沃缘。
因為Node.js是JavaScript的Runtime,所以天然就可以使用這種模式通知主進程的I/O 完成则吟。

3槐臀、非阻塞式 I/O(Non-blocking I/O)

阻塞:I/O 時進程休眠等待 I/O 完成后進行下一步
非阻塞:I/O 時函數(shù)立即返回,進程不等待I/O 完成

一氓仲、Node.js 究竟好在哪里水慨?

1、為什么偏愛Node.js

① 前端需求變得重要敬扛、職責范圍變大讥巡,統(tǒng)一開發(fā)體驗
② 在處理高并發(fā)、I/O 密集型場景性能優(yōu)勢明顯

Node.js 使用了事件驅動和非阻塞的 I/O 模型舔哪,使 Node 輕量高效欢顷,非常適合 I/O 密集的 Web 場景。

CPU密集型 VS I/O密集型
CPU密集型:計算等邏輯判斷的操作捉蚤,如:壓縮抬驴、解壓、加密和解密等缆巧。
I/O 密集型:存取設備布持,網絡設施的讀取操作,如:文件的存取陕悬,http等網絡操作题暖,數(shù)據庫操作等。

2捉超、Web常見場景

① 靜態(tài)資源讀取
html胧卤,css,js等文件的讀取
② 數(shù)據庫操作
把數(shù)據存取到物理設磁盤或內存中
③ 渲染頁面
讀取模板文件拼岳,根據數(shù)據生成html

3枝誊、高并發(fā)應對之道

高并發(fā),簡而言之就是單位時間內訪問量特別大惜纸。

對應生活中的場景叶撒,一家菜館做菜招待顧客绝骚,老板剛開始就雇了一個廚師,做菜好吃不貴祠够,顧客很多压汪,顧客排好一條隊,然后顧客選好菜古瓤,廚師拿到菜單開始做菜蛾魄,做好菜,給顧客端上來湿滓,再招待下個顧客滴须。

一個廚師應對若干顧客

客人增多,一個廚師忙不過來了叽奥,老板于是又招了2個廚師扔水,這樣顧客可以排3條隊,快了很多朝氓。

多個廚師應對若干顧客

隨著菜館名氣增大魔市,顧客越來越多,老板本想再用之前的方法多招幾個廚師赵哲,但是老板想待德,多招廚師好像不太劃算,有些廚師做飯快枫夺,有些做飯慢将宪,經過調查,老板發(fā)現(xiàn)做飯快2倍的廚師只需要花費原來廚師工資的1.5倍橡庞,于是精明的老板炒掉了原來的3個廚師较坛,招來了比原來廚師做飯速度快2倍的另外3個廚師,菜館比之前運轉的更好了扒最。

多個速度快的廚師應對若干顧客

回到Web開發(fā)場景丑勤,廚師就是物理服務器,應對高并發(fā)的方法如下:① 增加機器數(shù)
機器多了吧趣,流量還是一樣的大法竞,通過Nginx負載均衡分到不同的機器上處理
② 增加每臺機器的CPU數(shù)——多核
單位機器,核數(shù)增多强挫,運算能力增強了

4岔霸、進程與線程

進程在百度百科中的解釋如下:

進程(Process)是計算機中的程序關于某數(shù)據集合上的一次運行活動,是系統(tǒng)進行資源分配和調度的基本單位纠拔。

換成正常的人話就是:電腦桌面的程序秉剑,如QQ音樂泛豪,當我們雙擊圖標時稠诲,實際上是把這個程序加載到內存中執(zhí)行侦鹏,我們稱這個執(zhí)行中的程序就是進程,操作系統(tǒng)都是用進程作為基本單位進行分配和調度的臀叙。

多進程:啟動多個進程略水,多個進程可以一塊執(zhí)行多個任務。

線程劝萤,進程內一個相對獨立的渊涝、可調度的執(zhí)行單元,與同屬一個進程的線程共享進程的資源床嫌。

多線程跨释,啟動一個進程,在一個進程內啟動多個線程厌处,這樣鳖谈,多個線程也可以一塊執(zhí)行多個任務。

5阔涉、Node.js工作模型

傳統(tǒng)的server處理請求(如多線程高并發(fā)模式的Apache)對應生活中的場景缆娃,如下:
一個老板開了一家飯店,不同于之前那個菜館瑰排,這家的每個廚師配備了一個服務員贯要,專門負責點菜,然后把菜單給廚師椭住,廚師負責做菜崇渗,做完后給服務員,服務員端給客人京郑,然后再接待顧客隊伍中的下一個显押。

如果這個飯店是Web的話,點菜這個動作很快傻挂,相當于CPU的運算乘碑,如訪問一個靜態(tài)資源,CPU運算后知道是哪個文件了金拒,去相應盤讀取兽肤,類似于廚師做飯,是一個相對較慢的阻塞I/O操作绪抛,當顧客很多時候就相當于高并發(fā)了资铡。忙不過來的時候,可以選擇增加廚師數(shù)量和服務員數(shù)量幢码,即并發(fā)多進程處理多個請求的概念笤休。

一個服務員每次只接待一個顧客

但是這個飯店老板慢慢發(fā)現(xiàn),增加服務員和廚師的同時症副,飯店的空間是有限的店雅,慢慢的變得擁擠(阻塞)政基,而且更為頭疼的是另一個問題:服務員太悠閑了,2分鐘就把點菜的事干完了闹啦,廚師做菜10分鐘沮明,那他就有8分鐘在那干等著,沒事干窍奋,因為廚師沒把菜做完給他荐健,他也不能接待下一個顧客。

同樣類似于Apache開發(fā)web時候琳袄,CPU分配的最大進程數(shù)是有限的江场,并不能沒完沒了的分配進程的,并發(fā)到一定數(shù)目的時候窖逗,必須得排隊(阻塞)了扛稽,更大的問題是,CPU處理的速度遠遠快于I/O滑负,在Web場景中在张,CPU運用場景很少,大頭都在I/O上矮慕,CPU大部分進程情況下都是在等待帮匾,等待I/O,CPU的資源被浪費掉了痴鳄,相當于飯店的服務員一直干等著沒事干瘟斜。

Node.js很好的解決了上面的問題??????,來看下Node.js對應的生活中的場景,如下:

一個服務員每次接待多個顧客

另一個老板也開了一家飯店痪寻,這家飯店只雇傭了一個服務員螺句,這個服務員接待所有的顧客,顧客來了依次點菜橡类,點完菜拿個號找地方坐著去了蛇尚,然后服務員把菜單交給廚師們,然后再去給下一波客人點菜下單顾画,等后廚什么時候說取劫,服務員幾號的菜好了,然后服務員端好菜給制定號碼的顧客研侣,無論哪個顧客來了立刻響應谱邪,廚師一直做菜,服務員一直接單庶诡,顧客的體驗也比上一家要好惦银,不用等到上一個顧客拿到菜才開始點單等待。對應Web體驗就是:一直在那轉圈等待,要比直接顯示連不上要好扯俱。

同樣在Node.js中书蚪,用戶(顧客)發(fā)來請求,有一個主進程(服務員)蘸吓,對應有一個事件輪詢(Event Loop)善炫,來處理用戶的各種請求過來的進程(菜單)撩幽,如qq音樂(小蔥拌豆腐)库继,Photoshop(魚香肉絲)等,然后給Worker threads即多線程(廚師)的去處理窜醉,處理完后完成回調(上菜)宪萄,CPU的利用率達到最大。在Node.js中榨惰,一個CPU上只開一個進程拜英,一個進程里只有一個線程

Node.js工作模型(圖片源于網絡)
6琅催、Node.js單線程

Node.js單線程指的是居凶,一個CPU上只開一個進程,一個進程里只有一個線程藤抡。但這個單線程只是針對主進程侠碧,I/O 等其它各種異步操作都是操作系統(tǒng)底層在多線程調度的 。Node.js就是主進程發(fā)起一個請求缠黍,請求交給I/O 之后弄兜,是由操作系統(tǒng)底層多進程多線程進行調度,然后達到最佳性能瓷式。

Node.js是單線程的替饿,但是不要理解為它做所有事都是單線程的,有一部分不是自己做的贸典,而是交給操作系統(tǒng)做的视卢,它只負責單進程的在那,操作系統(tǒng)好了廊驼,就告訴它單進程的做另外的事情腾夯,操作系統(tǒng)怎么處理I/O,它不管蔬充。

單線程并不就是單進程蝶俱,Node.js有個多核處理模塊叫cluster,專門用來處理多CPU饥漫,CPU如果有8個核榨呆,用了cluster之后,Node.js就啟了8個進程庸队,不會浪費CPU的能力积蜻。

7闯割、Node.js能干嘛
  • Web Server
  • 本地代碼編譯構建(grunt、babel等工具都是基于Node.js開發(fā)的)
  • 實用工具的開發(fā)(爬蟲等)

三竿拆、Node.js的基礎API

1宙拉、path(路徑)

path 模塊提供了一些工具函數(shù),用于處理文件與目錄的路徑丙笋⌒怀海可以通過以下方式使用:
const path = require('path');
path常用方法:
path.normalize(path)
會規(guī)范化給定的 path,并解析 '..' 和 '.' 片段,如:

const { normalize } = require('path'); 

console.log(normalize.('/usr///local/bin'));   //  /usr/local/bin
console.log(normalize.('/usr//local/../bin'));  //  /usr/bin

/*或者這樣寫:
const path = require('path'); 
console.log(path.normalize.('/usr///local/bin')); 
*/

path.join([...paths])
使用平臺特定的分隔符把全部給定的 path 片段連接到一起御板,并規(guī)范化生成的路徑,也能解析 '..' 和 '.' 锥忿,如:

const { join } = require('path');

console.log(join.('/usr', 'local', 'bin/'));   //  /usr/local/bin
console.log(join.('/usr', '../local', 'bin/'));  //  /usr/bin

path.resolve([...paths])
會把一個路徑或路徑片段的序列解析為一個絕對路徑,如:

const { resolve } = require('path');

console.log(resolve.('./'));   //  /Users/peng/Desktop 返回當前路徑的絕對路徑

path.basename(path[, ext])
返回文件名
path.dirname(path) 返回所在文件夾名
path.extname(path) 返回擴展名

const { basename, dirname, extname } = require('path');

const filePath = '/usr/local/bin/test.html';
console.log(basename.(filePath));   //  test.html
console.log(dirname.(filePath));   //  /usr/local/bin
console.log(extname.(filePath));   //  .html

path.parse(path)
返回一個對象怠肋,對象的屬性表示 path 的元素
path.format() 會從一個對象返回一個路徑字符串敬鬓。 與 path.parse()方法相反

const { parse, format } = require('path');

const filePath = '/usr/local/bin/test.html';
const ret = parse(filePath);
console.log(ret);
/*
{ root: '/',
   dir: '/usr/local/bin',
   base: 'test.html',
  ext: '.html',
  name: 'test' }
*/
console.log(format(ret));  // /usr/local/bin/test.html

另外:
__dirname__filename總是返回文件的絕對路徑
process.cwd()總是返回執(zhí)行node命令所在的文件夾

2笙各、Buffer (緩沖)

Buffer 類被引入作為 Node.js API 的一部分钉答,使其可以在 TCP 流或文件系統(tǒng)操作等場景中處理二進制數(shù)據流。

Buffer 類的實例類似于整數(shù)數(shù)組杈抢,但 Buffer 的大小是固定的数尿、且在 V8 堆外分配物理內存。 Buffer 的大小在被創(chuàng)建時確定春感,且無法調整砌创。

Buffer 類在 Node.js 中是一個全局變量(global),因此無需使用require('buffer').Buffer鲫懒。

常用方法:
Buffer.byteLength()
返回一個字符串的實際字節(jié)長度嫩实。 這與 String.prototype.length不同,因為那返回字符串的字符數(shù)窥岩。

console.log(Buffer.byteLength('test'));   // 4
console.log(Buffer.byteLength('中國'));  // 6

Buffer.from(array)
通過一個八位字節(jié)的 array 創(chuàng)建一個新的 Buffer 甲献,如果 array 不是一個數(shù)組,則拋出 TypeError 錯誤颂翼。

console.log(Buffer.from([1, 2, 3]));  // <Buffer 01 02 03>

Buffer.isBuffer(obj)
如果 obj 是一個 Buffer 則返回 true 晃洒,否則返回 false

console.log(Buffer.isBuffer({ 'a': 1 }));                      // false
console.log(Buffer.isBuffer(Buffer.from([1, 2, 3])));  // true

Buffer.concat(list)
如果 obj 是一個 Buffer 則返回 true ,否則返回 false

const buf1 = Buffer.from('hello ');
const buf2 = Buffer.from('world');

const buf = Buffer.concat([buf1, buf2]);

console.log(buf.toString());    // hello world

常用屬性:
buf.length 長度
buf.toString() 轉為字符串
buf.fill() 填充
buf.equals() 判斷是否相等
buf.indexOf() 是否包含朦乏,如果包含返回位置值球及,不包含返回-1

const buf = Buffer.from('hello world');
const buf2 = Buffer.from('hello world!');

console.log(buf.length);   // 15
console.log(buf.toString());   // hello world
console.log(buf.fill(10, 2, 6));  // <Buffer 68 65 0a 0a 0a 0a 77 6f 72 6c 64>   這里從第3個到第6個都被替換成了0a,a就是16進制的數(shù)字10
console.log(buf.equals(buf2));  // false
console.log(buf.indexOf('h'));  // 0
3、events(事件)

大多數(shù) Node.js 核心 API 都采用慣用的異步事件驅動架構呻疹,其中某些類型的對象(觸發(fā)器)會周期性地觸發(fā)命名事件來調用函數(shù)對象(監(jiān)聽器)吃引。

所有能觸發(fā)事件的對象都是 EventEmitter 類的實例。 這些對象開放了一個 eventEmitter.on() 函數(shù),允許將一個或多個函數(shù)綁定到會被對象觸發(fā)的命名事件上镊尺。 事件名稱通常是駝峰式的字符串朦佩,但也可以使用任何有效的 JavaScript 屬性名。

官網例子:一個綁定了一個監(jiān)聽器的 EventEmitter 實例庐氮。 eventEmitter.on() 方法用于注冊監(jiān)聽器语稠,eventEmitter.emit() 方法用于觸發(fā)事件。

const EventEmitter = require('events');

class CustomEvent extends EventEmitter {}

const myEmitter = new CustomEvent();

myEmitter.on('error', err => {
    console.log(err);
})

myEmitter.emit('error', new Error('This is an error!'));

當有一個錯誤的時候弄砍,會顯示Error: This is an error!仙畦,然后顯示具體錯誤內容。

4输枯、fs(文件系統(tǒng))

通過 require('fs') 使用該模塊议泵。 所有的方法都有異步和同步的形式占贫。

異步方法的最后一個參數(shù)都是一個回調函數(shù)桃熄。 傳給回調函數(shù)的參數(shù)取決于具體方法,但回調函數(shù)的第一個參數(shù)都會保留給異常型奥。 如果操作成功完成瞳收,則第一個參數(shù)會是 null 或 undefined。

常用方法如下:
fs.readFile(path[, options], callback)
異步地讀取一個文件的全部內容

const fs = require('fs');

fs.readFile('./test.txt', (err, data) => {
    if (err) throw err;
    console.log(data);
});

此時如果test.txt文件內容只有一個字母a厢汹,那么打印出來的就是<Buffer 61>
回調有兩個參數(shù) (err, data)螟深,其中 data 是文件的內容。如果未指定字符編碼烫葬,則返回原始的 buffer界弧。
指定編碼格式后,就會按照編碼格式打印文件內容:

const fs = require('fs');

fs.readFile('./test.txt', 'utf-8',(err, data) => {
    if (err) throw err;
    console.log(data);
});

此時如果test.txt文件內容只有一個字母a搭综,那么打印出來的就是a

fs.writeFile(file, data[, options], callback)
異步地寫入數(shù)據到文件垢箕,如果文件已經存在,則替代文件兑巾。

const fs = require('fs');

fs.writeFile('message.txt', 'Hello Node.js', (err) => {
    if (err) throw err;
    console.log('The file has been saved!');
});

fs.stat(path,callback)
可用來判斷一個文件是否存在
回調有兩個參數(shù) (err, stats)条获,其中 stats是一個 fs.Stats對象。

const fs = require('fs');

fs.stat('./message.txt', (err, stats)=>{
    if (err){
        console.log('文件不存在');
        return;
    };
    
    console.log(stats.isFile());     // true  判斷是否是一個文件
    console.log(stats.isDirectory());  // false  判斷是否是一個文件夾
});

fs.rename(oldPath, newPath, callback)
用來修改文件名

const fs = require('fs');

fs.rename('./message.txt', 'm.txt', err=>{
    if (err) throw err;
    
    console.log('修改成功蒋歌!');
})

fs.unlink(path, callback)
刪除文件

const fs = require('fs');

fs.unlink('./m.txt', err=>{
    if (err) throw err;
    
    console.log('刪除成功帅掘!');
})

fs.readdir(path[, options], callback)
讀取指定路徑下的所有文件

const fs = require('fs');

fs.readdir('./', (err, files)=>{
    if (err) throw err;
    
    console.log(files);
  /*
  [ '.DS_Store',
    'node_modules',
    'package.json',
    'test.js',
    'test.txt' ]
*/
})

fs.mkdir(path[, mode], callback)
在指定路徑里創(chuàng)建一個文件夾

const fs = require('fs');
// 在當前目錄創(chuàng)建一個叫test的文件夾
fs.mkdir('./test', err=>{
    if (err) throw err;
    
    console.log('文件夾創(chuàng)建成功');
})

fs.rmdir(path, callback)
刪除指定路徑下的文件夾

const fs = require('fs');

fs.rmdir('./test', err=>{
    if (err) throw err;
    
    console.log('文件夾刪除成功');
})

fs.watch(filename[, options][, listener])
和gulp里的watch很像,用來監(jiān)視 filename的變化堂油,filename 可以是一個文件或一個目錄修档。

監(jiān)聽器回調有兩個參數(shù) (eventType, filename)。 eventType 可以是 'rename' 或 'change'府框,filename 是觸發(fā)事件的文件的名稱吱窝。

const fs = require('fs');

fs.watch('./', {
    recursive: true   // 指明是否全部子目錄應該被監(jiān)視
}, (eventType, filename) =>{
   console.log(eventType, filename);
})

注意,在大多數(shù)平臺,當一個文件出現(xiàn)或消失在一個目錄里時癣诱,'rename' 會被觸發(fā)计维。

fs.createReadStream(path[, options])
返回一個新建的 ReadStream 對象

const fs = require('fs');

const rs = fs.createReadStream('./test.txt');
rs.pipe(process.stdout);  // 在終端輸出test.txt內容
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市撕予,隨后出現(xiàn)的幾起案子鲫惶,更是在濱河造成了極大的恐慌,老刑警劉巖实抡,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欠母,死亡現(xiàn)場離奇詭異,居然都是意外死亡吆寨,警方通過查閱死者的電腦和手機赏淌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啄清,“玉大人六水,你說我怎么就攤上這事±弊洌” “怎么了掷贾?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荣茫。 經常有香客問我想帅,道長,這世上最難降的妖魔是什么啡莉? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任港准,我火速辦了婚禮,結果婚禮上咧欣,老公的妹妹穿的比我還像新娘浅缸。我一直安慰自己,他們只是感情好该押,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布疗杉。 她就那樣靜靜地躺著,像睡著了一般蚕礼。 火紅的嫁衣襯著肌膚如雪烟具。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天奠蹬,我揣著相機與錄音朝聋,去河邊找鬼。 笑死囤躁,一個胖子當著我的面吹牛冀痕,可吹牛的內容都是我干的荔睹。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼言蛇,長吁一口氣:“原來是場噩夢啊……” “哼僻他!你這毒婦竟也來了?” 一聲冷哼從身側響起腊尚,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤吨拗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后婿斥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劝篷,經...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年民宿,在試婚紗的時候發(fā)現(xiàn)自己被綠了娇妓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡活鹰,死狀恐怖哈恰,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情华望,我是刑警寧澤蕊蝗,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布仅乓,位于F島的核電站赖舟,受9級特大地震影響,放射性物質發(fā)生泄漏夸楣。R本人自食惡果不足惜宾抓,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望豫喧。 院中可真熱鬧石洗,春花似錦、人聲如沸紧显。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孵班。三九已至涉兽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間篙程,已是汗流浹背枷畏。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留虱饿,地道東北人拥诡。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓触趴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親渴肉。 傳聞我的和親對象是個殘疾皇子冗懦,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內容

  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測試 ...
    KeKeMars閱讀 6,313評論 0 6
  • 個人入門學習用筆記、不過多作為參考依據仇祭。如有錯誤歡迎斧正 目錄 簡書好像不支持錨點批狐、復制搜索(反正也是寫給我自己看...
    kirito_song閱讀 2,458評論 1 37
  • Node.js是目前非城涂ⅲ火熱的技術盖腕,但是它的誕生經歷卻很奇特。 眾所周知咐扭,在Netscape設計出JavaScri...
    w_zhuan閱讀 3,612評論 2 41
  • Node.js是目前非郴火熱的技術食零,但是它的誕生經歷卻很奇特。 眾所周知寂屏,在Netscape設計出JavaScri...
    Myselfyan閱讀 4,066評論 2 58
  • 寒蟬凄切贰谣,對長亭晚,驟雨初歇迁霎。 都門帳飲無緒吱抚,留戀處,蘭舟摧發(fā)考廉。 執(zhí)手相看淚眼秘豹,竟無語凝噎。 念去去千里煙波昌粤,暮靄...
    morningao閱讀 187評論 0 0