nodejs(二) --- 重要知識點回顧
1. 運行一個nodejs文件,
如一個js文件中只含有console.log("hello world");的文件,我們再git裏運行node,即 node hello.js 即可發送輸出hello world。如下:
2. 交互模式
直接輸入node,即進入node環境,即可輸入任何語句:
3. 創建一個簡單的服務器:
創建server.js -> require http模塊 -> 調用 createServer 方法進行創建 -> 監聽某個端口 -> 運行js文件(實際上是在運行這個node服務器) -> 在瀏覽器中發送請求。
js文件如下:
其中比較重要的是要知道createServer這個內置函數就是用於創建服務器的,然後接受一個函數作爲參數,我們可以用writeHead方法來寫頭部,使用end方法來輸出內容。用listen來監聽端口號。當然,也可以是監聽8889等端口號,只要是合法的就行。在瀏覽器端發出請求,結果如下:
4. node中的npm:
安裝node時就已經安裝了npm,即一個包管理工具,我們一般利用它來安裝一些包,即npm install <package> 如果後面添加 -g ,那麼就會將包安全到全局環境,即user路徑下。如果不添加,就會安裝在當前文件夾下,當然首先會創建node_modules文件,在此之下。
一般,我們要創建一個項目時,我們可以先npm init,通過它,我們就可以創建一個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 <package>
更新模塊: npm update <package>
版本號相關: 當使用npm下載和發佈代碼時,都會涉及到版本號相關,即X-Y-Z,一般而言,只有當版本發生了重大的變化,不向下兼容時,X纔會變化; 如果增加了新的功能,仍舊向下兼容 Y 發生變化; 如果僅僅是做了很小的改變,如修復了bug,那麼 Z 發生變化。
如果希望知道某條命令的詳細信息,如install的,可以輸入 npm help install
npm連接的是國外的網站,也可以使用淘寶鏡像:
1 |
|
然後就可以使用cnpm來安裝了。
5. nodejs REPL
即node的交互式解釋器,即我們輸入node之後即進入node環境,這個node環境就是node的交互式解釋器。
值得注意的是,和console控制檯不同,REPL支持多行輸入,如下所示:
$ node > var x = 0 undefined > do { ... x++; ... console.log("x: " + x); ... } while ( x < 5 ); x: 1 x: 2 x: 3 x: 4 x: 5 undefined >
一般我們使用ctrl + c來退出該環境,大多數情況下,我們都是用ctrl+c來退出環境的。不論是否是node。
6. nodejs回調函數
在node中,我們知道其最大的特點就是異步I/O,而實現異步I/O的關鍵就在於回調函數。
首先,我們看看同步的是什麼樣:
var fs = require("fs"); var data = fs.readFileSync('test.txt'); console.log(data.toString()); console.log("over");
這是main.js中的代碼,即先引入fs模塊,然後才能實用相應的API,readFileAsync()函數用於同步讀取文件,它是阻塞的,返回這個讀取的文件,輸出如下:
下面這個一個異步的,
var fs = require("fs"); fs.readFile("test.txt",function (err, data) { if (err) { console.log(err); } else { console.log(data.toString()); } }); console.log("over");
結果如下:
即readFile是一個異步的,在執行這條語句的時候不會阻塞後面的語句,而是在讀完文件之後再console。
即我們在讀取代碼的時候可以做下面的很多事情,這樣就可以節省很多時間。
readFile和readFileSync都接受第二個參數,即編碼類型,如"utf-8"
7. nodejs事件循環
nodejs事件循環利用的是觀察者模式,也就是發佈訂閱模式。簡單的理解,DOM元素綁定事件就是這樣的模式。其中綁定的元素是發佈者,函數是訂閱者,當元素髮生了變化時(被點擊等),就會通知所有的訂閱者。
nodejs使用事件驅動模型,當服務器接受到了請求之後,就會關閉這個請求,然後再處理,爲的是等待下一個請求。這樣,請求就不會被耽擱。這個模型的效率非常高,因爲他一直在接受請求,而沒有等待任何讀寫操作。在事件驅動模型中,會生成一個主循環來監聽事件,當檢測到事件時觸發回調函數:
雖然這裏沒有所謂的DOM元素,但是實現應當是一樣的,即觀察者模式的最好理解是好萊塢電影中的一句話: 你不要打電話給我,我會打電話給你。
在node中我們常常使用events模塊來實現,即首先引入events,然後創建一個對象,利用這個對象的on方法綁定時間,利用對象的emit方法來觸發事件,如下所示:
var events = require("events"); var eventEmitter = 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");
最終效果如下:
在這裏,我們就可以認爲這是發佈訂閱者模式,首先可以知道發佈者是selfDefine事件,訂閱者是4個,一旦selfDefine被觸發,那麼就會通知訂閱者,方法是將訂閱者添加到了Event Loop中去,然後通過事件循環來監聽,一旦被觸發,就會通知,即我給你打電話,你沒有給我打電話。
當事件觸發時,註冊到這個事件的事件監聽器被依次調用。
另外,在on和emit中是可以傳遞參數的,如下所示:
var events = require("events"); var eventEmitter = 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是類似的,都是添加某一個事件的監聽器。
removeListener(event, listener) --- 即通過此API可以將監聽器取消(特定的listener)。
removeAllListeners(event) --- 可以取消event下的所有監聽器。
newListener(event, listener); --- 該事件在添加新的監聽器時被觸發。
listenerCount(emitter, event); --- 返回指定監聽器的數量。
listeners(event) --- 返回指定事件的監聽器數組。
once(event, listener) --- 通過once就可以知道,這個監聽器只會監聽一次,後面再調用,就不會監聽了。
舉例如下:
var events = require("events"); var eventEmitter = 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);
最終的執行效果如下:
在執行過程中vai on和via once是同時出現的,過了1s之後,via on 出現, via once不再出現,因爲通過once添加的監聽器只會監聽一次,然後就被銷燬了(即後面不再監聽)。
8. Nodejs Buffer (緩衝區)
作爲服務器端語言的nodejs,自然會接受請求,如TCP請求,都是通過二進制來傳遞的,但是js語言本身並沒有接受二進制的api,所以nodejs中添加了Buffer類來作爲存儲二進制數據的緩衝區。
通過Buffer類創建buffer實例的幾種方法:
1. 創建長度爲10字節(1 B = 8 bit)的Buffer類, var buf = new Buffer(10);
2. 通過數組創建Buffer實例, var buf = new Buffer([10, 20, 30, 15]);
3. 通過一個字符串來創建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是緩衝區開始寫入的索引值,默認爲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對象。其中list是一個數組,其中的每個元素是一個buffer實例,totalLength是在制定合併之後的總長度。
- buf.compare(otherBuffer) (緩衝區大小比較) --- 比較兩個緩衝區的大小,返回 0 -1 1 。
- buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]) (緩衝區的拷貝)
- buf.slice([start[, end]])(緩衝區的裁剪)
- buf.length() --- 返回緩衝區的長度。
- ......
9. Nodejs Stream(流)
Stream是一個抽象的接口,並且它是eventEmitter的實例,通常Stream有四種流類型, 包括可讀、可寫、可讀可寫、操作被寫入然後讀出。
既然它是eventEmitter的實例,那麼就會有事件,這個事件當然不再是自定義然後使用emit的方式,而是已經定義的,不再需要emit了。 有 data 、end、error、finish。其中data表示只要發現有數據就會立即觸發, end表示沒有更多的數據可讀時就會觸發, error是在讀或寫發生錯誤的時候觸發,finish在所有的數據被寫入底層系統時觸發。
因爲流中的讀寫都是與文件相關,所以需要引入fs模塊。讀:
var fs = require("fs"); var readStream = fs.createReadStream("./test.txt"); readStream.setEncoding("utf-8"); var data = ""; readStream.on("data", function (chunk) { data += chunk; }); readStream.on("end", function () { console.log(data +" FINISHED"); }); readStream.on("error", function (err) { console.log(err); });
可以看到首先引入文件系統fs,然後使用 createReadStream()方法來讀取文件, 綁定了data之後,只要文件中有內容就會被觸發,當讀取文件內容結束之後,就會執行end下的監聽器。 在讀的過程中有錯,就會執行error下的監聽器。
下面是寫操作:(其中的test.js現在內容爲空)
var fs = require("fs"); var writeStream = fs.createWriteStream("./test.txt"); var data = "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()方法表示結束,然後當寫入完成之後就會觸發finish, 最後,我們打開test.txt就會發現確實已經寫入了data數據 。
管道流:它提供了這樣的一個機制 --- 從一個流中讀取數據,然後輸入到另一個流中。
var fs = require("fs"); var readStream = fs.createReadStream("./input.txt"); var writeStream = fs.createWriteStream("./output.txt"); readStream.pipe(writeStream); console.log("finished");
通過這種方式,我們就可以將input.txt中的內容流到output.txt之中了。
鏈式流:鏈式是通過連接輸出流到另外一個流並創建多個對個流操作鏈的機制。鏈式流一般用於管道操作。
引入zlib模塊進行壓縮文件, 之所以說是鏈式流,是因爲我們可以連續使用pipe(),如下所示:
var fs = require("fs"); var zlib = require("zlib"); var readStream = fs.createReadStream("./foo.txt"); readStream.pipe(zlib.createGzip()).pipe(fs.createWriteStream("./foo.min.txt"));
即我們首先引入fs模塊和壓縮庫zlib,然後再創建一個讀流,通過管道流pipe到壓縮文件,然後再pipe到一個將要保存壓縮文件的寫文件。最後可以看到foo.min.txt是被壓縮過的。(注意:其中foo.min.txt不需要自己來寫,他會自動生成)
當然我們還可以用createGunzip()方法來解壓縮。
10. nodejs模塊系統
一個nodejs文件就是一個模塊。我們之前使用var fs = require("fs"); 這裏就是引入了一個fs模塊。因爲fs是內置的,所以直接引入就好。但是如果是我們自己創建的一個foo.js文件,我們希望引用這個模塊,就可以使用var foo = require("./foo"); 即引入當然文件目錄下的foo.js中的模塊。 其中js是默認的,省略不寫。
我們接觸到的require是nodejs提供的一個接受對象,與之相對的是exports倒出對象。
比如我們創建一個foo.js,這就是一個模塊,內容如下:
function Foo() { this.sayHello = function () { console.log("hello world!"); } } module.exports = Foo;
即這導出了一個構造函數。 然後我們就可以引入這個module了,如下:
var Foo = require("./foo"); var myFoo = new Foo(); myFoo.sayHello()
這裏我們require到了這個模塊,然後創建了實例,調用了模塊的方法。
服務器端的模塊:之前我們使用的require("http")就是在引入服務器端的模塊。 然後再直接調用即可,如createServer()方法。
nojs加載模塊方式如下:
第一步: 判斷文件模塊緩存區中是否存在模塊。(對於我們之前加載過的模塊,會緩存到緩存區中,下次最先查找並加載)。
第二步: 判斷是否是原生模塊,如http、fs等這就是原生模塊。 如果判斷一個模塊是原生模塊就會優先加載原生模塊。即即使我們有了一個http.json文件,但是由於原生模塊的優先級更高,所以優先加載。
第三步: 判斷是否是自定義的模塊。 即我們自己設定的模塊。
11. nodejs函數
nodejs中的函數也可以作爲另一個函數的參數,同樣也有匿名函數的概念,如下所示:
var http = require("http"); http.createServer(function (request, response) { response.writeHead("200", {"Content-Type": "text/plain"}); response.write("zhuzhenwei,you are handsome!"); response.end(); }).listen(8888);
這樣就創建了一個服務器。
注意:其中在writeHead中最好寫上charset=utf-8; 後面的字符也可以是utf8、UTF8、UTF-8。 他們都是有效的的。
例如下面的這個函數,和js中的作用域是一樣的,注意其中我們需要將Content-Type的內容寫成text/plain; charset=utf8; 否則在瀏覽器中輸出漢字時會有問題。
var http = 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內容如下:
var http = require("http"); var otherfun = 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等方法。
調用的時候顯然就是下面這樣的,調用對象的方法:
var http = require("http"); var fun = 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"); var Timer = 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是非阻塞的,所以後面的語句先執行,指的注意的是其中的console.log("%d",19950628)的應用,這與C語言中的printf是非常相似的。另外console.trace()可以追蹤調用棧。
另外還有一個比較重要的api,即setImmediate(handler); 他是IE10中支持的。其他的瀏覽器一律不支持,但是node是支持的,這個解決單線程阻塞的問題,當然用setTimeout(handler, 0);也可以,但是後者的延遲時間較前者更長一些。
process也是一個全局變量,在node環境下輸入global.process就可以看到其中具有的變量,因爲process本身就是一個對象。不難理解process是描述進程(process即進程的意思)的一個全局對象。
他還有一些事件,如下:
舉例如下:
setImmediate(function () { console.log("god"); }); process.on("exit", function (code) { console.log("exitCode is:", code); }); console.log("over");
即進程一旦結束,就會觸發監聽器。 注意:這裏使用on的方式,所以我們可以認爲process是eventEmitter的實例。
輸入如下:
退出碼爲0是什麼意思呢? 因爲每當exit事件觸發,都會有一個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),
即這個方法可以實現繼承,但是這裏的繼承和我們使用js實現的繼承也有不同之處,主要區別是這裏的繼承是原型對象之間的繼承,而不會繼承上一級的構造函數,舉例如下:
var util = 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); var objSup = new Sup(); console.log(objSup.name); var objSub = 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。舉例如下:
var util = 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()是一樣的,舉例如下:
var util = require("util"); console.log(util.isArray([])); //true console.log(util.isArray(new Array()));//true console.log(util.isArray({}));//true
util.isRegExp() 即判斷是否是一個正則表達式
util.isDate() 即判斷是否是一個日期
util.isError() 即判斷是否是一個錯誤對象
15. nodejs 文件系統
nodejs作爲後臺語言,必然不可避免的需要和數據庫文件等打交道,所以文件系統模塊是非常必要的。即file system --- 文件系統。
在node中的fs中,所有的方法均有同步和異步之分。如同步讀取文件,fs.readFile();異步讀取文件,fs.readFileSync()。如下所示:
var fs = require("fs"); // 異步讀取 fs.readFile('input.txt', function (err, data) { if (err) { return console.error(err); } console.log("異步讀取: " + data.toString()); }); // 同步讀取 var data = fs.readFileSync('input.txt'); console.log("同步讀取: " + data.toString()); console.log("程序執行完畢。");
ok! 下面介紹一些node中常用的文件操作api。
打開文件:
var fs = 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+ 讀寫方式打開文件,如果沒有,就創建
- 。。。
獲取文件的相關信息:
舉例如下:
輸入如下所示:
var fs = 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 } true false
可以看出fs.stat()接受兩個參數,第一個是文件名,第二個是一個回調函數,回調函數中有兩個參數 ,第一個參數是err,即發生錯誤時我們return,並且console.log(err),所以最終是return console.log(err); 第二個參數
是stats,實際上是fs.stats對象,這個對象中包含了很多關於這個文件的詳細信息---包括uid、gid、size、birthtime等。
寫入文件:
var fs = 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,然後寫文件函數接受三個參數,第一個是將要寫的文件(如果之前沒有,就創建之後再寫入),第二個是要寫入的內容, 第三個是一個匿名函數,如果出錯,就返回錯誤,否則,讀取文件,其中函數的第二個參數data即文件的內容,利用toString()就能還原。
創建目錄:
var fs = require("fs"); fs.mkdir("./new", function (err) { if (err) { return console.log(err); } console.log("seccess!"); });
這樣,就可以在當前目錄下創建一個new文件夾了。 注意: 最好使用相對路徑。
讀取目錄:
var fs = 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的用法:
var arr = [1,2,3,4,5,6,7,8,9]; arr.forEach(function (value) { console.log(value); });
即forEach用於數組,它接受一個函數作爲參數,函數的參數就是要遍歷的數組中的每一個值,最終的結果如下:
刪除目錄:
var fs = require("fs"); fs.rmdir("./ha", function (err) { if (err) { return console.log(err); } console.log("success"); });
注意:刪除目錄時,只能刪除文件夾中沒有文件的文件夾,否則無法刪除成功。那麼怎麼刪除文件呢?如下:
刪除文件:
var fs = require("fs"); fs.unlink("./foo.txt", function (err) { if (err) { return console.log(err); } console.log("seccess!"); });
這樣就可以刪除掉當前頁面下的foo.txt文件。 即unlink,不再相關的意思。如果希望刪除其他文件夾下的文件也非常簡單,如下:
var fs = require("fs"); fs.unlink("./some/g.txt", function (err) { if (err) { return console.log(err); } console.log("seccess!"); });
即修改相對路徑即可完成。
關閉文件:
fd---file descriptor 文件描述符
var fs = require("fs"); var buf = new Buffer(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); } // 僅輸出讀取的字節 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請求內容:
var http = require("http"); var util = require("util"); var url = 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的效果是一樣的。
var http = require("http"); var url = require("url"); http.createServer(function (req, res) { res.writeHead(200,{"Content-Type":"text/plain"}); var params = 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請求的頁面如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>for_post</title> </head> <body> <form method="post"> name: <input type="text" name="name"> <br> age: <input type="text" name="age"> <br><br> <input type="submit"> </form> </body> </html>
nodejs的文件如下:
var http = require("http"); var fs = require("fs"); var querystring = require("querystring"); http.createServer(function (req, res) { var body = ""; 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("<br>"); 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模塊 --- 提供基本的系統操作函數
Path模塊 --- 提供了處理和轉換文件的工具
Net模塊 --- 用於底層的網絡通信,提供了網絡端和服務器端的模塊
Dns模塊 --- 用於解析域名
Domain模塊 --- 簡化異步代碼的異步處理,可以捕捉try-catch無法捕捉的。
OS模塊:
var os = require("os"); // 操作系統的默認臨時文件夾 console.log(os.tmpdir()); //C:\Users\ADMINI~1\AppData\Local\Temp // 操作系統的字節序 console.log(os.endianness()); // LE // 操作系統的主機名 console.log(os.hostname()); //718JVP8AUMCTA6H // 操作系統名 console.log(os.type()); // window_NT 注意XP等系統都是基於window_NT發展的 // 操作系統 console.log(os.platform()); // win32 // 操作系統的發行版本 console.log(os.release()); // 10.0.14393 我的系統是win10的 // 系統內存總量 console.log(os.totalmem()); // 8475926528 // 系統的空閒內存 console.log(os.freemem()); // 4304961536 // CPU的信息 console.log(os.cpus());
CPU信息如下:
即四核的CPU,基本相同,urse略有差別 。
Path()模塊:
這個模塊主要給了一些處理路徑的api,如規範化路徑、拼接路徑、返回後綴名等,舉例如下:
var path = 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模塊中也有不少內容,但是還不知道具體的作用,下面的例子應該是常用的:
var dns = 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創建web服務器端:
大多數web服務器都支持服務器端腳本語言,如ruby、python、php等,並通過腳本語言,從數據庫獲取數據,將結果返回給客戶端瀏覽器, 目前主流的服務器是Apache、Nginx和IIS。
當然,這裏講的是node, 使用node就可以創建一個服務器。即引入http模塊創建。
下面的演示是一個最基本的服務器架構:
var http = require("http"); var fs = require("fs"); var url = require("url"); http.createServer(function (req, res) { var path = 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創建web客戶端:
注意:服務端還用之前的server.js,下面爲client.js
var http = require("http"); var options = { host: "localhost", port: "8081", path: "/index.html" }; var callback = function (res) { var body = ""; res.on("data", function (data) { body += data; }); res.on("end", function () { console.log(body); }); }; var req = http.request(options, callback); req.end();
首先在終端運行server.js,然後再打開一個終端,運行client.js,可以發現輸出如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> <style> h1 { color: red; } </style> </head> <body> <h1>hello, this is the index.html.</h1> </body> </html>
19. nodejs Express框架:
jQuery是客戶端js的框架,同樣Express是服務器端nodejs的框架,它提供了一系列的強大特性幫助我們快速構建web應用,也提供了豐富的http工具。使用Express可以快速搭建一個功能完整的網站。
Express的核心特性是:
- 設置中間件來響應http請求。
- 制定路由表,來響應不同的http請求操作。
- 通過向模板傳遞參數,動態渲染html。
更多內容看《nodejs 之 Express 框架》。
20. nodejs RESTful API:
REST 即表述性狀態轉移, Representional State Transfer, 這是一種軟件架構風格。表述性狀態轉移是一組架構約束條件和原則。滿足這些條件的程序就是RESTful,或者說基於REST架構的web service就是RESTful。
創建RESTful:
21. nodejs 多進程
引入child_process模塊就可以使用它的api來創建多進程。