1.?運行一個nodejs文件积蜻,
如一個js文件中只含有console.log("hello world");的文件,我們再git里運行node玫坛,即 node hello.js 即可發(fā)送輸出hello world。如下:
2.?交互模式
直接輸入node,即進入node環(huán)境昆码,即可輸入任何語句:
3.?創(chuàng)建一個簡單的服務器:
創(chuàng)建server.js -> require http模塊 -> 調用 createServer 方法進行創(chuàng)建 -> 監(jiān)聽某個端口 -> 運行js文件(實際上是在運行這個node服務器) -> 在瀏覽器中發(fā)送請求。
js文件如下:
其中比較重要的是要知道createServer這個內置函數就是用于創(chuàng)建服務器的邻储,然后接受一個函數作為參數赋咽,我們可以用writeHead方法來寫頭部,使用end方法來輸出內容芥备。用listen來監(jiān)聽端口號冬耿。當然,也可以是監(jiān)聽8889等端口號萌壳,只要是合法的就行亦镶。在瀏覽器端發(fā)出請求,結果如下:
4.?node中的npm:
安裝node時就已經安裝了npm袱瓮,即一個包管理工具缤骨,我們一般利用它來安裝一些包,即npm install 如果后面添加 -g 尺借,那么就會將包安全到全局環(huán)境绊起,即user路徑下。如果不添加燎斩,就會安裝在當前文件夾下虱歪,當然首先會創(chuàng)建node_modules文件,在此之下栅表。
一般笋鄙,我們要創(chuàng)建一個項目時,我們可以先npm init怪瓶,通過它萧落,我們就可以創(chuàng)建一個package.json文件,然后通過配置該文件說明我們的項目信息洗贰。其中主要的參數有:
(1)name --- 包名 ? (2)version --- 版本號 ?(3) description --- 包的描述 ? (4)?homepage?--- 包的官網url ?(5)?author?--- 包的作者名字 ?(6)?contributors?--- 包的其他貢獻者名字
(7)dependencies? --- 依賴包列表(如果依賴包沒有安裝找岖,npm 會自動將依賴包安裝在 node_module 目錄下) ? ?(8)repository?--- 包代碼存放的地方 ? (9)main ---?main 字段是一個模塊ID,它是一個指向你程序的主要項目敛滋。就是說许布,如果你包的名字叫 express,然后用戶安裝它绎晃,然后require("express") ? ?(9) keywords --- 關鍵字
卸載模塊: npm uninstall
更新模塊: npm update
版本號相關: 當使用npm下載和發(fā)布代碼時爹脾,都會涉及到版本號相關帖旨,即X-Y-Z,一般而言,只有當版本發(fā)生了重大的變化灵妨,不向下兼容時解阅,X才會變化; 如果增加了新的功能泌霍,仍舊向下兼容 Y 發(fā)生變化货抄; 如果僅僅是做了很小的改變,如修復了bug朱转,那么 Z 發(fā)生變化蟹地。
如果希望知道某條命令的詳細信息,如install的藤为,可以輸入 npm help install
npm連接的是國外的網站怪与,也可以使用淘寶鏡像:
1npm install -g cnpm --registry=https://registry.npm.taobao.org
?然后就可以使用cnpm來安裝了。
5. nodejs REPL
即node的交互式解釋器缅疟,即我們輸入node之后即進入node環(huán)境分别,這個node環(huán)境就是node的交互式解釋器。
值得注意的是存淫,和console控制臺不同耘斩,REPL支持多行輸入,如下所示:
$ node>varx =0undefined>do {
... x++;
... console.log("x: "+ x);
... } while( x <5 );
x: 1x: 2x: 3x: 4x: 5undefined>
一般我們使用ctrl + c來退出該環(huán)境桅咆,大多數情況下括授,我們都是用ctrl+c來退出環(huán)境的。不論是否是node岩饼。
6. nodejs回調函數
在node中荚虚,我們知道其最大的特點就是異步I/O,而實現(xiàn)異步I/O的關鍵就在于回調函數籍茧。
首先版述,我們看看同步的是什么樣:
varfs = require("fs");vardata = fs.readFileSync('test.txt');
console.log(data.toString());
console.log("over");
這是main.js中的代碼,即先引入fs模塊硕糊,然后才能實用相應的API院水,readFileAsync()函數用于同步讀取文件腊徙,它是阻塞的简十,返回這個讀取的文件,輸出如下:
下面這個一個異步的撬腾,
? ? varfs = require("fs");
? ? fs.readFile("test.txt",function (err, data) {
? ? ? ? if (err) {
? ? ? ? ? ? console.log(err);
? ? ? ? } else {
? ? ? ? ? ? console.log(data.toString());
? ? ? ? }
? ? });
? ? console.log("over");
結果如下:
即readFile是一個異步的螟蝙,在執(zhí)行這條語句的時候不會阻塞后面的語句,而是在讀完文件之后再console民傻。
即我們在讀取代碼的時候可以做下面的很多事情胰默,這樣就可以節(jié)省很多時間场斑。
readFile和readFileSync都接受第二個參數,即編碼類型牵署,如"utf-8"
7. nodejs事件循環(huán)
nodejs事件循環(huán)利用的是觀察者模式漏隐,也就是發(fā)布訂閱模式。簡單的理解奴迅,DOM元素綁定事件就是這樣的模式青责。其中綁定的元素是發(fā)布者,函數是訂閱者取具,當元素發(fā)生了變化時(被點擊等)脖隶,就會通知所有的訂閱者。?
nodejs使用事件驅動模型暇检,當服務器接受到了請求之后产阱,就會關閉這個請求,然后再處理块仆,為的是等待下一個請求构蹬。這樣,請求就不會被耽擱榨乎。這個模型的效率非常高怎燥,因為他一直在接受請求,而沒有等待任何讀寫操作蜜暑。在事件驅動模型中铐姚,會生成一個主循環(huán)來監(jiān)聽事件,當檢測到事件時觸發(fā)回調函數:
雖然這里沒有所謂的DOM元素肛捍,但是實現(xiàn)應當是一樣的隐绵,即觀察者模式的最好理解是好萊塢電影中的一句話:?你不要打電話給我,我會打電話給你拙毫。
在node中我們常常使用events模塊來實現(xiàn)依许,即首先引入events,然后創(chuàng)建一個對象缀蹄,利用這個對象的on方法綁定時間峭跳,利用對象的emit方法來觸發(fā)事件,如下所示:
varevents = require("events");vareventEmitter =new events.EventEmitter();
eventEmitter.on("selfDefine", function () {
? ? console.log("This is selfDefine1");
});
eventEmitter.on("selfDefine", function () {
? ? console.log("This is selfDefine2");
});
eventEmitter.on("selfDefine", function () {
? ? console.log("This is selfDefine3");
});
eventEmitter.on("selfDefine", function () {
? ? console.log("This is selfDefine4");
});
eventEmitter.emit("selfDefine");
console.log("over");
最終效果如下:
在這里缺前,我們就可以認為這是發(fā)布訂閱者模式蛀醉,首先可以知道發(fā)布者是selfDefine事件,訂閱者是4個衅码,一旦selfDefine被觸發(fā)显歧,那么就會通知訂閱者慈鸠,方法是將訂閱者添加到了Event Loop中去包个,然后通過事件循環(huán)來監(jiān)聽,一旦被觸發(fā)割捅,就會通知,即我給你打電話帚桩,你沒有給我打電話亿驾。
當事件觸發(fā)時,注冊到這個事件的事件監(jiān)聽器被依次調用账嚎。
另外颊乘,在on和emit中是可以傳遞參數的,如下所示:
varevents = require("events");vareventEmitter =new events.EventEmitter();
eventEmitter.on("selfDefine", function (x) {
? ? console.log("This is selfDefine1 "+ x);
});
eventEmitter.on("selfDefine", function (x) {
? ? console.log("This is selfDefine2 "+ x);
});
eventEmitter.on("selfDefine", function (x) {
? ? console.log("This is selfDefine3 "+ x);
});
eventEmitter.on("selfDefine", function (x) {
? ? console.log("This is selfDefine4 "+ x);
});
eventEmitter.emit("selfDefine","argument");
console.log("over");
最終的效果如下:
關于 EventEmitter 還有其他的屬性醉锄,如下所示:
addListener(event, listener) --- 它和on是類似的乏悄,都是添加某一個事件的監(jiān)聽器。
removeListener(event, listener) --- 即通過此API可以將監(jiān)聽器取消(特定的listener)恳不。
removeAllListeners(event) --- 可以取消event下的所有監(jiān)聽器檩小。
newListener(event, listener); --- 該事件在添加新的監(jiān)聽器時被觸發(fā)。
listenerCount(emitter, event); --- 返回指定監(jiān)聽器的數量烟勋。
listeners(event) --- 返回指定事件的監(jiān)聽器數組规求。
once(event, listener) ---?通過once就可以知道,這個監(jiān)聽器只會監(jiān)聽一次卵惦,后面再調用阻肿,就不會監(jiān)聽了。
舉例如下:
varevents = require("events");vareventEmitter =new events.EventEmitter();
eventEmitter.on("foo", function () {
? ? console.log("via on");
});
eventEmitter.once("foo", function () {
? ? console.log("via once");
});
eventEmitter.emit("foo");
setTimeout(function () {
? ? eventEmitter.emit("foo");
}, 1000);
最終的執(zhí)行效果如下:
在執(zhí)行過程中vai on和via once是同時出現(xiàn)的沮尿,過了1s之后丛塌,via on 出現(xiàn), via once不再出現(xiàn)畜疾,因為通過once添加的監(jiān)聽器只會監(jiān)聽一次赴邻,然后就被銷毀了(即后面不再監(jiān)聽)。
8. Nodejs Buffer (緩沖區(qū))
作為服務器端語言的nodejs啡捶,自然會接受請求姥敛,如TCP請求,都是通過二進制來傳遞的瞎暑,但是js語言本身并沒有接受二進制的api彤敛,所以nodejs中添加了Buffer類來作為存儲二進制數據的緩沖區(qū)。
通過Buffer類創(chuàng)建buffer實例的幾種方法:
1. 創(chuàng)建長度為10字節(jié)(1 B = 8 bit)的Buffer類了赌, var buf = new Buffer(10);
2. 通過數組創(chuàng)建Buffer實例墨榄, var buf = new Buffer([10, 20, 30, 15]);
3. 通過一個字符串來創(chuàng)建buffer實例, var buf = new Buffer("i love coding", "utf-8"); 注意: 我們這里使用utf-8格式編碼揍拆,還可以是"ascii", ?"utf16le", "ucs2", "base64" 和 "hex"渠概,當然茶凳,默認就是utf-8嫂拴。
已經有了buffer實例播揪,我們就可以使用buffer實例的一些方法了,如下所示:
write()(寫入數據) ---buf.write(string[,?offset[,?length]][,?encoding])筒狠。它的返回值是寫入的長度猪狈。 ?我們知道[]表示式可選的, 其中string是將要寫入的字符串辩恼; offset是緩沖區(qū)開始寫入的索引值雇庙,默認為0;length是長度灶伊,默認是buf.length疆前; encoding是編碼方式,默認是utf-8聘萨。
toString() (讀取數據)--- buf.toString([encoding[,?start[,?end]]])竹椒。它的返回值是讀取的值。其中的encoding表示讀取數據的編碼方式米辐, start和end表示讀取數據的位置胸完。
toJSON() (轉換為JSON對象)--- buf.toJSON(buf)。 返回值是一個JSON對象翘贮。
Buffer.concat(list[,totalLength]) (合并Buffer對象) --- 返回值是合并后的buffer對象赊窥。其中l(wèi)ist是一個數組,其中的每個元素是一個buffer實例狸页,totalLength是在制定合并之后的總長度锨能。
buf.compare(otherBuffer) (緩沖區(qū)大小比較) --- ?比較兩個緩沖區(qū)的大小,返回 0 -1 1 芍耘。
buf.copy(targetBuffer[,?targetStart[,?sourceStart[,?sourceEnd]]]) (緩沖區(qū)的拷貝)
buf.slice([start[,?end]])(緩沖區(qū)的裁剪)
buf.length() --- 返回緩沖區(qū)的長度腹侣。
......
9. Nodejs Stream(流)
Stream是一個抽象的接口,并且它是eventEmitter的實例齿穗,通常Stream有四種流類型傲隶, 包括可讀、可寫窃页、可讀可寫跺株、操作被寫入然后讀出。
既然它是eventEmitter的實例脖卖,那么就會有事件乒省,這個事件當然不再是自定義然后使用emit的方式,而是已經定義的畦木,不再需要emit了袖扛。 有 data 、end、error蛆封、finish唇礁。其中data表示只要發(fā)現(xiàn)有數據就會立即觸發(fā), end表示沒有更多的數據可讀時就會觸發(fā)惨篱, error是在讀或寫發(fā)生錯誤的時候觸發(fā)盏筐,finish在所有的數據被寫入底層系統(tǒng)時觸發(fā)。
因為流中的讀寫都是與文件相關砸讳,所以需要引入fs模塊琢融。讀:
varfs = require("fs");varreadStream = fs.createReadStream("./test.txt");
readStream.setEncoding("utf-8");vardata ="";
readStream.on("data", function (chunk) {
? ? data += chunk;
});
readStream.on("end", function () {
? ? console.log(data +" FINISHED");
});
readStream.on("error", function (err) {
? ? console.log(err);
});
可以看到首先引入文件系統(tǒng)fs,然后使用 createReadStream()方法來讀取文件簿寂, 綁定了data之后漾抬,只要文件中有內容就會被觸發(fā),當讀取文件內容結束之后常遂,就會執(zhí)行end下的監(jiān)聽器奋蔚。 在讀的過程中有錯,就會執(zhí)行error下的監(jiān)聽器烈钞。
下面是寫操作:(其中的test.js現(xiàn)在內容為空)
varfs = require("fs");varwriteStream = fs.createWriteStream("./test.txt");vardata ="I want to write something";
writeStream.write(data);
writeStream.end();
writeStream.on("finish", function () {
? ? console.log("finished");
});
writeStream.on("error", function (err) {
? ? console.log(err);
});
console.log("ok!");
? 最終輸出為: ok! ? finished
注意: 我們需要使用end()方法表示結束泊碑,然后當寫入完成之后就會觸發(fā)finish, 最后毯欣,我們打開test.txt就會發(fā)現(xiàn)確實已經寫入了data數據 馒过。
管道流:它提供了這樣的一個機制 --- 從一個流中讀取數據,然后輸入到另一個流中酗钞。
varfs = require("fs");varreadStream = fs.createReadStream("./input.txt");varwriteStream = fs.createWriteStream("./output.txt");
readStream.pipe(writeStream);
console.log("finished");
通過這種方式腹忽,我們就可以將input.txt中的內容流到output.txt之中了。
鏈式流:鏈式是通過連接輸出流到另外一個流并創(chuàng)建多個對個流操作鏈的機制砚作。鏈式流一般用于管道操作窘奏。
引入zlib模塊進行壓縮文件, 之所以說是鏈式流葫录,是因為我們可以連續(xù)使用pipe()着裹,如下所示:
varfs = require("fs");varzlib = require("zlib");varreadStream = fs.createReadStream("./foo.txt");
readStream.pipe(zlib.createGzip()).pipe(fs.createWriteStream("./foo.min.txt"));
即我們首先引入fs模塊和壓縮庫zlib,然后再創(chuàng)建一個讀流米同,通過管道流pipe到壓縮文件骇扇,然后再pipe到一個將要保存壓縮文件的寫文件。最后可以看到foo.min.txt是被壓縮過的面粮。(注意:其中foo.min.txt不需要自己來寫少孝,他會自動生成)
當然我們還可以用createGunzip()方法來解壓縮。
10. nodejs模塊系統(tǒng)
一個nodejs文件就是一個模塊熬苍。我們之前使用var fs = require("fs"); 這里就是引入了一個fs模塊稍走。因為fs是內置的,所以直接引入就好。但是如果是我們自己創(chuàng)建的一個foo.js文件婿脸,我們希望引用這個模塊粱胜,就可以使用var foo = require("./foo"); 即引入當然文件目錄下的foo.js中的模塊。 其中js是默認的盖淡,省略不寫。
我們接觸到的require是nodejs提供的一個接受對象凿歼,與之相對的是exports倒出對象褪迟。
比如我們創(chuàng)建一個foo.js,這就是一個模塊答憔,內容如下:
function Foo() {
? ? this.sayHello = function () {
? ? ? ? console.log("hello world!");
? ? }
}
module.exports = Foo;
即這導出了一個構造函數味赃。 然后我們就可以引入這個module了,如下:
varFoo = require("./foo");varmyFoo =new Foo();
myFoo.sayHello()
這里我們require到了這個模塊虐拓,然后創(chuàng)建了實例心俗,調用了模塊的方法。
服務器端的模塊:之前我們使用的require("http")就是在引入服務器端的模塊蓉驹。 然后再直接調用即可城榛,如createServer()方法。
nojs加載模塊方式如下:
第一步: 判斷文件模塊緩存區(qū)中是否存在模塊态兴。(對于我們之前加載過的模塊狠持,會緩存到緩存區(qū)中,下次最先查找并加載)瞻润。
第二步: 判斷是否是原生模塊喘垂,如http、fs等這就是原生模塊绍撞。 如果判斷一個模塊是原生模塊就會優(yōu)先加載原生模塊正勒。即即使我們有了一個http.json文件,但是由于原生模塊的優(yōu)先級更高傻铣,所以優(yōu)先加載章贞。
第三步: 判斷是否是自定義的模塊。 即我們自己設定的模塊非洲。
11. nodejs函數
nodejs中的函數也可以作為另一個函數的參數阱驾,同樣也有匿名函數的概念,如下所示:
varhttp = require("http");
http.createServer(function (request, response) {
? ? response.writeHead("200", {"Content-Type":"text/plain"});
? ? response.write("zhuzhenwei,you are handsome!");
? ? response.end();
}).listen(8888);
這樣就創(chuàng)建了一個服務器怪蔑。
注意:其中在writeHead中最好寫上charset=utf-8; 后面的字符也可以是utf8里覆、UTF8、UTF-8缆瓣。 他們都是有效的的喧枷。
例如下面的這個函數,和js中的作用域是一樣的,注意其中我們需要將Content-Type的內容寫成text/plain; charset=utf8; 否則在瀏覽器中輸出漢字時會有問題隧甚。
varhttp = require("http");
http.createServer(function (req, res) {
? ? ? ? ? ? res.writeHead(200, {"Content-Type":"text/html; charset=utf8"});
? ? ? ? ? ? if(req.url !=="/favicon.ico") {// 清除第二次訪問console.log("訪問");
? ? ? ? ? ? ? ? a(res);
? ? ? ? ? ? ? ? res.write("Hello world!");
? ? ? ? ? ? ? ? res.end();?
? ? ? ? ? ? }
}).listen(8081);
function a(res) {
? ? res.write("hello, 我是一個被調用的函數车荔。");
}
console.log("Server running at http:127.0.0.1:8081");
其中的req.url !== "/favicon.ico"是為了解決自身的bug的。?
上面的這種方式是對于一個內部的函數而言的戚扳,但是如果我們希望是一個外部文件的函數應該怎么辦呢忧便??
如下,建立一個fun2.js帽借,和server.js在同一個文件下珠增,內容如下:
function a2(res) {
? ? res.write("我是fun2調用的函數");
}
module.exports = a2;
注意:最后一句的意思是我們希望將這個函數導出,如果不導出去砍艾,就沒有辦法使用蒂教。然后server.js內容如下:
varhttp = require("http");varotherfun = require("./fun2");
http.createServer(function (req, res) {
? ? ? ? ? ? res.writeHead(200, {"Content-Type":"text/html; charset=utf8"});
? ? ? ? ? ? if(req.url !=="/favicon.ico") {// 清除第二次訪問console.log("訪問");
? ? ? ? ? ? ? ? otherfun(res);
? ? ? ? ? ? ? ? res.write("Hello world!");
? ? ? ? ? ? ? ? res.end();?
? ? ? ? ? ? }
}).listen(8081);
console.log("Server running at http:127.0.0.1:8081");
注意: 其中如果我們要使用這個函數,就必須要用otherfun來調用脆荷,雖然在fun2中的函數名是fun2凝垛,但是在server.js中只認otherfun。?
另外蜓谋,因為這是一個本地的文件梦皮,我們最好在前面加上./ 表示相對位置。
缺點:可以看到這樣桃焕,我們每次只能在一個文件中導出一個函數届氢,但是對于一個文件中有多個函數的情況應該怎么導出呢? 如下所示:
module.exports = {
? ? func2 : function (res) {
? ? ? ? res.write("我是func2函數");
? ? },
? ? func3 : function (res) {
? ? ? ? res.write("我是func3函數");
? ? }
};
即將函數使用對象的形式定義覆旭,然后我們導出這么個對象退子,就向http一樣,我們引入這個http對象之后型将,然后使用http.createServer等方法寂祥。?
調用的時候顯然就是下面這樣的,調用對象的方法:
varhttp = require("http");varfun = require("./fun.js");
http.createServer(function (req, res) {
? ? ? ? ? ? res.writeHead(200, {"Content-Type":"text/html; charset=utf8"});
? ? ? ? ? ? if(req.url !=="/favicon.ico") {// 清除第二次訪問console.log("訪問");
? ? ? ? ? ? ? ? fun.func2(res);
? ? ? ? ? ? ? ? fun.func3(res);
? ? ? ? ? ? ? ? res.end();?
? ? ? ? ? ? }
}).listen(8081);
console.log("Server running at http:127.0.0.1:8081");
效果如下:
所以七兜,可以看到丸凭,后者可以調用多個函數,一般就用后面這種形式腕铸。
另外惜犀,我們還經常用字符串的形式,如fun["func2"]或者fun["func3"]狠裹,這樣的好處是:我們可以把調用寫活了虽界,在后面講到路由的時候更為重要,因為可以把字符串作為一個變量涛菠,輸入不同的值莉御,就可以調用不同的函數撇吞,?非常重要。礁叔。
12. nodejs路由(重點)
我們所需要的數據都在request對象中牍颈,另外,我們得先解析url琅关,需要引入url模塊和querystring模塊煮岁。
即url.parse()可以解析這個url。
對于客戶端輸入的url涣易,我們通過request.url即可獲取画机。
這里比較難理解。都毒。色罚。
13. nodejs全局對象
與瀏覽器中window作為全局不同碰缔,在node中global是全局對象账劲,我們可以直接在全局對象上定義屬性,那么就可以訪問到了金抡,指的注意的是瀑焦,由于每一個nodejs模塊都是一個作用域,所以直接var是局部變量梗肝,而不是全局變量榛瓮。
__filename --- 這個全局變量表示運行的nodejs的文件名。
__dirname --- 表示目錄名稱(不包含文件名)
在文件中的代碼如下:
console.log(__filename);
console.log(__dirname);
輸出如下:
可以看出巫击,其中__filename是包含了路徑的禀晓。 而__dirname僅僅是缺少了文件名,只有路徑坝锰。
另外粹懒,console、setTimeout顷级、clearInterval等等都是全局對象凫乖,舉例如下:
console.time("set");varTimer = setInterval(function () {
? ? console.log(__dirname);
}, 1000);
setTimeout(function () {
? ? clearInterval(Timer);
? ? console.log("cleared");
},5000);
console.timeEnd("set");
console.info("info");
console.warn("warn");
console.log("my birthday is %d",19950628);
最終的輸入如下:
可以看到,由于setInterval和setTimeout是非阻塞的弓颈,所以后面的語句先執(zhí)行帽芽,指的注意的是其中的console.log("%d",19950628)的應用,這與C語言中的printf是非常相似的翔冀。另外console.trace()可以追蹤調用棧导街。
另外還有一個比較重要的api,即setImmediate(handler); 他是IE10中支持的纤子。其他的瀏覽器一律不支持菊匿,但是node是支持的付呕,這個解決單線程阻塞的問題,當然用setTimeout(handler, 0);也可以跌捆,但是后者的延遲時間較前者更長一些狈邑。
process也是一個全局變量趁尼,在node環(huán)境下輸入global.process就可以看到其中具有的變量,因為process本身就是一個對象。不難理解process是描述進程(process即進程的意思)的一個全局對象城侧。
他還有一些事件,如下:
舉例如下:
setImmediate(function () {
? ? console.log("god");
});
process.on("exit", function (code) {
? ? console.log("exitCode is:", code);
});
console.log("over");
即進程一旦結束截型,就會觸發(fā)監(jiān)聽器蚕泽。 注意:這里使用on的方式,所以我們可以認為process是eventEmitter的實例钙姊。
輸入如下:
退出碼為0是什么意思呢? ?因為每當exit事件觸發(fā)毯辅,都會有一個code即退出碼,表示這個退出的方式煞额,0表示正常退出思恐,一般還有如下幾種退出碼:
process 不僅提供了上述事件,還提供了非常多的有關進程的屬性膊毁,如pid(進程號)胀莹、platform(程序運行的平臺)、archf(當前CPU的架構)婚温、title(進程名描焰,默認為node)、versions(包含了node的版本和依賴)栅螟、version(node的版本)荆秦、execPath(當前腳本的二進制文件路徑)、stdin力图、stdout步绸、stderr。舉例如下:
console.log(process.platform);
console.log(process.version);
process.stdout.write("hello world \n");
console.log(process.execPath);
注意:其中的stdout.write是在終端輸出搪哪,那么什么時候才能在頁面上輸出呢靡努? ?顯然,由于node是服務器端語言晓折,所以說只能通過響應(response)的方式才能返回給客戶端惑朦。
下面的process方法也是常用的:
console.log(process.memoryUsage());
console.log(process.cwd());
其中第一個是內存使用情況。后者是cwd(current working directory)即當前工作目錄漓概。
可以看到rss漾月、heapTotal、heapUsed表示了內存使用情況胃珍。
14. nodejs常用工具
這里需要介紹的工具梁肿,首先要引入util模塊蜓陌,主要有util.inherits、util.inspect吩蔑、util.isArray(object)钮热、util.isRegExp(object)、util.isDate(object)烛芬、util.isError(object)隧期。
util.inherits(subconstructor, supconstructor),
即這個方法可以實現(xiàn)繼承,但是這里的繼承和我們使用js實現(xiàn)的繼承也有不同之處赘娄,主要區(qū)別是這里的繼承是原型對象之間的繼承仆潮,而不會繼承上一級的構造函數,舉例如下:
varutil = require("util");
function Sup() {
? ? this.name ="sup";
? ? this.sayHello = function () {
? ? ? ? console.log(this.name);
? ? }
}
Sup.prototype.show = function () {
? ? console.log("just show yourself");
}
function Sub() {
? ? this.name ="sub";
}
util.inherits(Sub, Sup);varobjSup =new Sup();
console.log(objSup.name);varobjSub =new Sub();
objSub.show();// objSub.sayHello(); // 報錯遣臼, objSub.sayHello() is not a function.
這里可以看到其中的objSub.show()成功繼承了Sup的原型中的方法性置,但是objSub.sayHello()卻會報錯,因為通過util.inherits()的方式是不能繼承構造函數中的方法的揍堰,與js中的不一樣鹏浅,需要注意。
util.inspect(obj)
這里并不是只有一個參數个榕,它實際上還可以接受三個篡石,第二個是true/false芥喇,表示是否顯示更多的信息西采,第三個是depth,即遞歸的層數,默認是2層继控; 第四個是關于顏色的true/false械馆。舉例如下:
varutil = require("util");
function Foo() {
? ? this.age =21;
? ? this.name ='zzw';
? ? this.sayHello = function () {
? ? ? ? console.log(this.age);
? ? }
}
console.log(util.inspect(Foo));
console.log(util.inspect(Foo, true));
最終的輸出如下:
可以看到,沒有第二個參數武通,那么只會輸出簡單的一個函數霹崎,如果為true,就會輸出更多深層次的內容冶忱。
util.isArray():
實際上尾菇,這里和js中的Array.isArray()是一樣的,舉例如下:
varutil = require("util");
console.log(util.isArray([])); //trueconsole.log(util.isArray(newArray()));//trueconsole.log(util.isArray({}));//true
util.isRegExp() 即判斷是否是一個正則表達式
util.isDate() 即判斷是否是一個日期
util.isError() 即判斷是否是一個錯誤對象
15. nodejs 文件系統(tǒng)
nodejs作為后臺語言囚枪,必然不可避免的需要和數據庫文件等打交道派诬,所以文件系統(tǒng)模塊是非常必要的。即file system --- 文件系統(tǒng)链沼。
在node中的fs中默赂,所有的方法均有同步和異步之分。如同步讀取文件括勺,fs.readFile()缆八;異步讀取文件曲掰,fs.readFileSync()。如下所示:
varfs = require("fs");// 異步讀取fs.readFile('input.txt', function (err, data) {
? if (err) {
? ? ? return console.error(err);
? }
? console.log("異步讀取: "+ data.toString());
});// 同步讀取vardata = fs.readFileSync('input.txt');
console.log("同步讀取: "+ data.toString());
console.log("程序執(zhí)行完畢奈辰。");
ok栏妖! ?下面介紹一些node中常用的文件操作api。
打開文件:
varfs = require("fs");
console.log("準備打開文件");
fs.open("foo.txt","r+",function (err, fd) {
? ? if (err) {
? ? ? ? return? console.log(err);
? ? }
? ? console.log("成功打開文件");?
});
即第一個參數是要打開的文件的路徑奖恰,第二個參數是打開的flag(方式)底哥,第三個參數是回調函數,即如果打開錯誤房官,返回輸出錯誤趾徽,否則輸出成功打開文件。
其中flag有下面的方式:?
r 讀模式打開文件
r+ ?讀寫模式打開文件
rs 同步方式打開文件
rs+ 同步方式打開和讀寫文件
w ?寫入方式打開文件
w+ 讀寫方式打開文件翰守,如果沒有孵奶,就創(chuàng)建
。蜡峰。了袁。
獲取文件的相關信息:
舉例如下:
輸入如下所示:
varfs = require("fs");
fs.stat("foo.txt", function (err, stats) {
? ? if (err) {
? ? ? ? return console.log(err);
? ? }
? ? console.log(stats);
? ? console.log(stats.isFile());
? ? console.log(stats.isDirectory());
});
輸出如下所示:
$ node inherits
{ dev: 917962,
? mode: 33206,
? nlink: 1,
? uid: 0,
? gid: 0,
? rdev: 0,
? blksize: undefined,
? ino: 3377699720545815,
? size: 60,
? blocks: undefined,
? atime: 2017-04-12T12:30:03.350Z,
? mtime: 2017-04-12T12:30:33.144Z,
? ctime: 2017-04-12T12:30:33.144Z,
? birthtime: 2017-04-12T12:30:03.228Z }truefalse
可以看出fs.stat()接受兩個參數,第一個是文件名湿颅,第二個是一個回調函數载绿,回調函數中有兩個參數 ,第一個參數是err油航,即發(fā)生錯誤時我們return崭庸,并且console.log(err),所以最終是return console.log(err); 第二個參數
是stats,實際上是fs.stats對象谊囚,這個對象中包含了很多關于這個文件的詳細信息---包括uid怕享、gid、size镰踏、birthtime等函筋。
寫入文件:
varfs = require("fs");
fs.writeFile("input.txt","somethingsomethingsomething", function (err) {
? ? if (err) {
? ? ? ? return console.log(err);
? ? }
? ? console.log("seccess!");
? ? fs.readFile("input.txt", function (err, data) {
? ? ? ? if (err) {
? ? ? ? ? ? return console.log(err);
? ? ? ? }
? ? ? ? console.log(data.toString());
? ? });
});
?即這里首先引入了fs,然后寫文件函數接受三個參數奠伪,第一個是將要寫的文件(如果之前沒有跌帐,就創(chuàng)建之后再寫入),第二個是要寫入的內容绊率, 第三個是一個匿名函數谨敛,如果出錯,就返回錯誤即舌,否則佣盒,讀取文件,其中函數的第二個參數data即文件的內容顽聂,利用toString()就能還原肥惭。
創(chuàng)建目錄:
varfs = require("fs");
fs.mkdir("./new", function (err) {
? ? if (err) {
? ? ? ? return console.log(err);
? ? }
? ? console.log("seccess!");
});
?這樣盯仪,就可以在當前目錄下創(chuàng)建一個new文件夾了。 注意: 最好使用相對路徑蜜葱。?
讀取目錄:
varfs = require("fs");
fs.readdir("./some", function (err, files) {
? ? if (err) {
? ? ? ? return console.log(err);
? ? }
? ? files.forEach(function (file) {
? ? ? ? console.log(file);
? ? });
});
注意:在當前的some文件夾下有兩個txt文件全景,我們只要給readdir()的第二個參數傳入一個函數,第二個參數是files(即文件夾中的所有文件)牵囤,就可以通過forEach來遍歷輸出了爸黄。結果如下:
df.txt
g.txt
?這里回顧一下forEach的用法:
vararr = [1,2,3,4,5,6,7,8,9];
arr.forEach(function (value) {
? ? ? console.log(value);
});
即forEach用于數組,它接受一個函數作為參數揭鳞,函數的參數就是要遍歷的數組中的每一個值炕贵,最終的結果如下:
刪除目錄:
varfs = require("fs");
fs.rmdir("./ha", function (err) {
? ? if (err) {
? ? ? ? return console.log(err);
? ? }
? ? console.log("success");
});
注意:刪除目錄時,只能刪除文件夾中沒有文件的文件夾野崇,否則無法刪除成功称开。那么怎么刪除文件呢?如下:
刪除文件:
varfs = require("fs");
fs.unlink("./foo.txt", function (err) {
? ? if (err) {
? ? ? ? return console.log(err);
? ? }
? ? console.log("seccess!");
});
這樣就可以刪除掉當前頁面下的foo.txt文件乓梨。 即unlink鳖轰,不再相關的意思。如果希望刪除其他文件夾下的文件也非常簡單扶镀,如下:
varfs = require("fs");
fs.unlink("./some/g.txt", function (err) {
? ? if (err) {
? ? ? ? return console.log(err);
? ? }
? ? console.log("seccess!");
});
?即修改相對路徑即可完成蕴侣。
關閉文件:
fd---file descriptor 文件描述符
varfs = require("fs");varbuf =newBuffer(1024);
console.log("準備打開文件!");
fs.open('input.txt','r+', function(err, fd) {
? if (err) {
? ? ? return console.error(err);
? }
? console.log("文件打開成功臭觉!");
? console.log("準備讀取文件昆雀!");
? fs.read(fd, buf, 0, buf.length,0, function(err, bytes){
? ? ? if (err){
? ? ? ? console.log(err);
? ? ? }
? ? ? // 僅輸出讀取的字節(jié)if(bytes >0){
? ? ? ? console.log(buf.slice(0, bytes).toString());
? ? ? }
? ? ? // 關閉文件? ? ? fs.close(fd, function(err){
? ? ? ? if (err){
? ? ? ? ? ? console.log(err);
? ? ? ? }
? ? ? ? console.log("文件關閉成功");
? ? ? });
? });
});
其中input.txt中是有內容的,重要的是在open方法的參數中胧谈,第三個參數的第二個是fd---文件描述符忆肾,即必須通過文件描述符來read和close荸频。
16.?nodejs GET/POST 請求(重點):
nodejs是服務器端語言菱肖,當然要和瀏覽器打交道,所以處理get旭从、post請求是非常必要的稳强。
獲取get請求內容:
varhttp = require("http");varutil = require("util");varurl = require("url");
http.createServer(function (req, res) {
? ? res.writeHead(200,{"Content-Type":"text/plain"});
? ? res.end(util.inspect(url.parse(req.url)));
? ? console.log(url.parse(req.url).path);
}).listen(3000);
?這里引入了util模塊用于inspect,引入了url模塊用于parseGET請求的url和悦。 注意我們再parse之后得到的是一個對象退疫,這個對象中包含了req.url的詳細信息。瀏覽器效果如下:
即nodejs就相當于運行了本地服務器鸽素,我們使用localhost:3000即表示訪問我們本地服務器的3000端口褒繁。和127.0.0.1:3000的效果是一樣的。
varhttp = require("http");varurl = require("url");
http.createServer(function (req, res) {
? ? res.writeHead(200,{"Content-Type":"text/plain"});
? ? varparams= url.parse(req.url,true).query;
? ? res.write(params.name+"\n");
? ? res.write(params.age);
? ? res.end();
}).listen(3000);
最終的結果如下:
獲取POST請求內容:
使用post請求后馍忽,需要我們使用nodejs在后臺利用req.on和querystring.pase來手動解析post請求棒坏。
一個含有post請求的頁面如下:
? ? ? ? for_post? ? ? ? ? ? name:
? ? ?
? ? ? ? age:
? ? ? ? ? ?
nodejs的文件如下:
varhttp = require("http");varfs = require("fs");varquerystring = require("querystring");
http.createServer(function (req, res) {
? ? varbody ="";
? ? req.on("data", function (chunk) {
? ? ? ? body += chunk;
? ? });
? ? req.on("end", function () {
? ? ? ? body = querystring.parse(body);
? ? ? ? res.writeHead(200, {'Content-Type':'text/html; charset=utf8'});
? ? ? ? if(body.name && body.age) {
? ? ? ? ? ? console.log("true");
? ? ? ? ? ? res.write("The client is "+body.name);
res.write("
");? ? ? ? ? ? res.write(
"This clent is "+ body.age +" years old");
? ? ? ? ? ? res.end();
? ? ? ? } else {
? ? ? ? ? ? console.log("else");
? ? ? ? ? ? fs.readFile("./post.html", function (err, data) {
? ? ? ? ? ? ? ? if (err) {
? ? ? ? ? ? ? ? ? ? return console.log(err);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? res.write(data);
? ? ? ? ? ? ? ? res.end();
? ? ? ? ? ? });
? ? ? ? }
? ? });
}).listen(8888);
最終的效果如下:
good燕差! ?這個也很容易嘛!
17. nodejs工具模塊
?nodejs中提供了很多好用的模塊坝冕,如下:
OS模塊 --- 提供基本的系統(tǒng)操作函數
?Path模塊 --- 提供了處理和轉換文件的工具
Net模塊 --- ?用于底層的網絡通信徒探,提供了網絡端和服務器端的模塊
Dns模塊 --- 用于解析域名
Domain模塊 --- 簡化異步代碼的異步處理,可以捕捉try-catch無法捕捉的喂窟。
OS模塊:
varos = require("os");// 操作系統(tǒng)的默認臨時文件夾console.log(os.tmpdir());//C:\Users\ADMINI~1\AppData\Local\Temp// 操作系統(tǒng)的字節(jié)序console.log(os.endianness());// LE// 操作系統(tǒng)的主機名console.log(os.hostname());//718JVP8AUMCTA6H // 操作系統(tǒng)名console.log(os.type());// window_NT 注意XP等系統(tǒng)都是基于window_NT發(fā)展的// 操作系統(tǒng)console.log(os.platform());// win32// 操作系統(tǒng)的發(fā)行版本console.log(os.release());// 10.0.14393? 我的系統(tǒng)是win10的// 系統(tǒng)內存總量console.log(os.totalmem());// 8475926528// 系統(tǒng)的空閑內存console.log(os.freemem());// 4304961536// CPU的信息console.log(os.cpus());
CPU信息如下:
即四核的CPU测暗,基本相同,urse略有差別 磨澡。
Path()模塊:
這個模塊主要給了一些處理路徑的api碗啄,如規(guī)范化路徑、拼接路徑稳摄、返回后綴名等挫掏,舉例如下:
varpath = require("path");
console.log("normalization:"+ path.normalize('/test/test1//test2..'));
console.log("joinPath:"+ path.join('test','test1/','/test2'));
console.log("toAbsolute:"+ path.resolve("main.js"));
console.log("extName:"+ path.extname("main.js"));
結果如下所示:
normalization:\test\test1\test2..
joinPath:test\test1\test2
toAbsolute:C:\Users\Administrator\Desktop\node\main.js
extName:.js
Net()模塊:
這里的內容非常多,等需要的時候再深入學習秩命。
Dns模塊:
dns模塊中也有不少內容尉共,但是還不知道具體的作用,下面的例子應該是常用的:
vardns = require("dns");
dns.lookup("www.baidu.com", function onLookup(err, address) {
? ? if (err) {
? ? ? ? return console.log(err);
? ? }
? ? console.log(address);
});
這樣弃锐,就可以解析出www.baidu.com的域名袄友,通過address即可輸出119.75.217.109
Domain模塊:
這個模塊主要用于簡化異常處理。
18. nodejs Web 模塊
?使用node創(chuàng)建web服務器端:
? 大多數web服務器都支持服務器端腳本語言霹菊,如ruby剧蚣、python、php等旋廷,并通過腳本語言鸠按,從數據庫獲取數據,將結果返回給客戶端瀏覽器饶碘, 目前主流的服務器是Apache目尖、Nginx和IIS。
當然扎运,這里講的是node瑟曲, 使用node就可以創(chuàng)建一個服務器。即引入http模塊創(chuàng)建豪治。
下面的演示是一個最基本的服務器架構:
varhttp = require("http");varfs = require("fs");varurl = require("url");
http.createServer(function (req, res) {
? ? varpath = url.parse(req.url).pathname;
? ? fs.readFile(path.substr(1), function (err, data) {
? ? ? ? if (err) {
? ? ? ? ? ? console.log(err);
? ? ? ? ? ? res.writeHead(404, {"Content-Type":"text/html"});
? ? ? ? } else {
? ? ? ? ? ? res.writeHead(200, {"Content-Type":"text/html"});
? ? ? ? ? ? res.write(data.toString());
? ? ? ? ? ? res.end();
? ? ? ? }
? ? });
}).listen(8081);
console.log("Server running at http:127.0.0.1:8081");
演示如下:
?使用node創(chuàng)建web客戶端:
?注意:服務端還用之前的server.js洞拨,下面為client.js
varhttp = require("http");varoptions = {
? ? host: "localhost",
? ? port: "8081",
? ? path: "/index.html"};varcallback = function (res) {
? ? varbody ="";
? ? res.on("data", function (data) {
? ? ? ? body += data;
? ? });
? ? res.on("end", function () {
? ? ? ? console.log(body);
? ? });
};varreq = http.request(options, callback);
req.end();
首先在終端運行server.js,然后再打開一個終端负拟,運行client.js烦衣,可以發(fā)現(xiàn)輸出如下:
? ? ? ? index? ? ? ? ? ? h1 {
? ? ? ? ? ? color: red;
? ? ? ? }
? ? ? ?
hello,thisisthe index.html.
19. nodejs Express框架:
jQuery是客戶端js的框架,同樣Express是服務器端nodejs的框架,它提供了一系列的強大特性幫助我們快速構建web應用花吟,也提供了豐富的http工具启泣。使用Express可以快速搭建一個功能完整的網站。
Express的核心特性是:
設置中間件來響應http請求示辈。
制定路由表寥茫,來響應不同的http請求操作。
通過向模板傳遞參數矾麻,動態(tài)渲染html纱耻。
更多內容看《nodejs 之 Express 框架》。
20.?nodejs RESTful API:
?REST 即表述性狀態(tài)轉移险耀, Representional State Transfer弄喘, 這是一種軟件架構風格。表述性狀態(tài)轉移是一組架構約束條件和原則甩牺。滿足這些條件的程序就是RESTful蘑志,或者說基于REST架構的web service就是RESTful。
創(chuàng)建RESTful:
?21. nodejs 多進程
引入child_process模塊就可以使用它的api來創(chuàng)建多進程贬派。