node.js入門學習筆記整理——基礎篇

零、開始以前

一、 首先解釋一下node.js是什麼?

二、node.js和javascript有什麼不一樣?

1)由於javascript主要是用在browser,而node.js是在server或者你的電腦上寫,因此就不會有window / document這兩個變量,取而代之的是global / process.
global和window同樣,也有不少內置的function和variables,好比setTimeout() / setInterval() / console..., 不過也有不少不一樣,這個後面會慢慢講;javascript

1、Modules—— require() & exports

node.js裏很重要的一個概念就是modules。除了一些core modules(好比events/http/fs/url...), 還能夠用require()來引進一些自定義的modules。html

好比:java

//stuff.js
var counter = function(arr){
    return 'There are '+ arr.length+ ' arguments.'
   };

var adder = function(a,b){
    return `the sum is ${a+b};`
   };

var pi = 3.1415926;

若是要在其餘的file裏用這個counter function應該怎麼作呢?
在stuff.js後面加上:node

//第一種方法
//modules.exports是一個object對象
modules.exports.counter = counter;
modules.exports.adder = adder;
modules.exports.pi = pi;

上面的方法意思是到了,可是比較複雜,咱們能夠用對象的方式來寫modules.exportsjquery

//第二種方法
modules.exports = {
    counter: counter,
    adder: adder,
    pi: pi
  }

還有一種辦法直接寫在上面的functions裏:web

modules.exports.counter = function(arr){
    return 'There are '+ arr.length+ ' arguments.'
   };

modules.exports.adder = function(a,b){
    return `the sum is ${a+b};`
   };

modules.exports.pi = 3.1415926;

而後要引用的時候使用require():json

//main.js

var stuff = require('./stuff'); //能夠不用寫extension name,node本身會補上;
//使用一下:
console.log(stuff.counter(['hello', 'world']);
console.log(stuff.adder(5, stuff.pi);

2、EventEmitter類

在node.js裏,有一個core module是event,event裏只有一個對象,就是EventEmitter, 主要用於封裝事件觸發和事件監聽器的功能(使用javascript裏的觀察者模式)。
若是用過jquery的話,對這個格式必定不陌生:api

button.on('click', function(){alert('clicked!');}

其實node裏的EventEmitter和jQuery的這個事件監聽函數的寫法也很相似:app

//首先引入EventEmitter對象
//EventEmitter是一個constructor,可使用原型繼承
var eventEmitter = require('event').EventEmitter;
var event = new eventEmitter();
//綁定事件
event.on('data', function(str){
    console.log('something here: '+ str.toString());
   };
//觸發事件
//前一個param是event的名字,後一個是callback函數須要的變量
event.emit('data', 'Tom wants sth to eat.');

前面提到EventEmitter實際上是一個constructor,能夠經過prototype繼承;
咱們如今用util這個module來實現node裏的inherits:異步

var event = require('event');
var util = require('util');

//創造一個person的constructor,一會來繼承eventEmitter;
var Person = function(name){
    this.name = name;
   };
//util.inherits用來繼承,注意兩個params的順序;
util.inherits(Person, event.EventEmitter);

//如今咱們來new兩個person實例出來:
var james = new Person('james');
var ellen = new Person('ellen');
//放在一個叫people的array裏:
var people = [james, ellen];
//如今用eventEmitter裏的on方法綁定事件;
//由於Person實例已經繼承,因此可使用
people.forEach(function(person){
  person.on('speak', function(str){
    console.log(`${person} says ${str}`);
  });
});
//觸發事件
james.emit('speak', 'good morning!');
ellen.emit('speak', 'good evening!');

3、fs (file system) 類

fs類主要是用來處理文件的,有很是多的方法,但在這個小教程裏只涉及read和write兩項。
首先咱們來區分一下「同步」和「異步」的概念:
「同步」—— blocking; 解析器會一步一步地解析你的code,若是前面那行沒操做完就不會操做後面那行;
「異步」——non-blocking;解析器解析完這行代碼以前,不會妨礙到後面代碼的進行,在異步處理完後經過callback函數對結果進行處理,所以,異步的性能比同步會不少。

//node中同步read和write的寫法:
var fs = require('fs');
//同步是'readFileSync'
var readMe = fs.readFileSync('input.txt', 'utf8');
fs.write('output.txt', readMe);

//異步:
//err是出現的錯誤,data是readFile後讀取到的數據
fs.readFile('input.txt', 'utf8', function(err, data){
    if(err){
        console.error(err);
     }else{
        fs.writeFile('output.txt', data);
     }

能夠看到,運用fs的callback函數,能夠直接在readFile裏寫writeFile, 不須要從新定義變量。

再寫一個fs中的創建目錄和移除目錄的方法,一樣有同步和異步兩種方式:

var fs = require('fs');

//make a directory sync
fs.mkdirSync('stuff');
//delete a directory sync
fs.rmdirSync('stuff');
//make a directory async
fs.mkdir('stuff', function(){
    fs.readFile('readMe.txt', 'utf8', function(err,data){
        fs.writeFile('./stuff/writeMe/txt', data);
    });
});
//remove a directory async
//you must first remove all files in this directory, then you can delete this folder
fs.unlink('./stuff/writeMe.txt', function(){
    fs.rmdir('stuff');
})

4、Stream類

在解釋stream以前,首先咱們來想想爲何要有readable stream和writable stream呢? 明明fs類裏就有readFile和writeFile的方法啊,何須還要再添加stream來找麻煩呢?

要知道這兩個之間的不一樣,讓咱們來理一下"buffer"的概念。
buffer的介紹通常是這樣:

在Node.js中,Buffer類是隨Node內核一塊兒發佈的核心庫。Buffer庫爲Node.js帶來了一種存儲原始數據的方法,可讓Nodejs處理二進制數據,每當須要在Nodejs中處理I/O操做中移動的數據時,就有可能使用Buffer庫。

簡單來講,buffer就是一個處理二進制數據的緩衝模塊,那和stream又有什麼關係呢?
能夠看下下面的圖:
圖片描述
這張圖裏,能夠把很大的一塊data進行肢解,而後儲存到buffer中,造成一個個的相對比較小的data chunk;
圖片描述
在這張圖裏,chunk one by one向前傳遞就成了stream。

因此回到咱們最初的問題,用readable stream和fs裏的readFile有什麼不一樣?

readFile是等一個file所有讀完以後才fire callback函數,進行下一步動做;而stream是經過把file裏的數據分紅不少不少個小的chunk, 每次callback函數感知到data chunk的時候就會觸發。這樣傳遞數據會更快。

一、Readable stream

var fs  = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/input.txt', 'utf8'); 
//這裏的‘utf8'若是不加的話會解碼成二進制(由於buffer)

myReadStream.on('data', function(chunk){
    console.log('new chunk received');
    console.log(chunk);
});
//stream也繼承eventEmitter
//每次感知到data chunk就會觸發,不須要等到整個file都讀完

二、Writable stream

//和readable stream相似
//會創造出來一個output.txt在目錄下
var fs  = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/input.txt', 'utf8');
var myWriteStream = fs.createWriteStream(__dirname + '/output.txt');

myReadStream.on('data', function(chunk){
    console.log('new chunk received');
    myWriteStream.write(chunk);
});

三、pipe

由於把readable stream轉成writable stream在node.js裏很是常見,因此有一個更elegant的辦法就是用pipe:

var fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/input.txt', 'utf8');
var myWriteStream = fs.createWriteStream(__dirname + '/output.txt');
//pipe只能從可讀流出發,不能從可寫流出發
myReadStream.pipe(myWriteStream);

若是加上web server就能夠這麼寫:

var fs  = require('fs');
var http = require('http');

var server = http.createServer(function(req, res){
    console.log('request was made: '+ req.url);
    res.writeHead(200, {'Content-Type': 'text/plain'});
    var myReadStream = fs.createReadStream(__dirname + '/input.txt', 'utf8');
    myReadStream.pipe(res);
    //response obj is writable 
});

server.listen(3000);
console.log('listen to port 3000');

若是要傳遞的數據是html格式的話:

var fs  = require('fs');
var http = require('http');

var server = http.createServer(function(req, res){
    console.log('request was made: '+ req.url);
    //html格式也能夠用stream傳遞,用pipe把可讀流轉成可寫流
    res.writeHead(200, {'Content-Type': 'text/html'});
    var myReadStream = fs.createReadStream(__dirname + '/index.html', 'utf8');
    myReadStream.pipe(res);
    //response obj is writable 
});

server.listen(3000);
console.log('listen to port 3000');

若是傳遞的數據是json的話:

//不使用stream,直接在res.end()裏面傳遞
//可是要注意的是,end()裏面只接受string,不能把object直接放進去
var fs  = require('fs');
var http = require('http');

var server = http.createServer(function(req, res){
    console.log('request was made: '+ req.url);
    res.writeHead(200, {'Content-Type': 'application/json'});
    var myobj = {
        name: 'Ryu',
        job: 'ninja',
        age: 29
    };
    res.end(JSON.stringify(myobj));
});

server.listen(3001);
console.log('listen to port 3001');

5、Router

介紹一個很簡單的router的使用辦法,只要用if判斷req.url,而後用stream寫入便可:

var http = require('http');
var fs = require('fs');

var server = http.createServer(function(req,res){
    console.log('request was made: '+ req.url);
    if(req.url === '/home' || req.url === '/'){
        res.writeHead(200, {'Content-Type' : 'text/html'});
//用fs的pipe把readable stream改爲writable stream
        fs.createReadStream(__dirname +'/index.html').pipe(res);
    }else if(req.url === '/sample'){
        res.writeHead(200, {'Content-Type': 'text/html'});
        fs.createReadStream(__dirname + '/sample.html').pipe(res);
    }else if(req.url === '/api/ninjas'){
        var ninjas = [{name: 'ryu', age: 29}, {name: 'yoshi',age:32 }];
        res.writeHead(200, {'Content-Type': 'application/json'});
        res.end(JSON.stringify(ninjas));
    }
//其實還能夠再寫一個404 page
});

server.listen(3000);
console.log('now listening to port 3000');
相關文章
相關標籤/搜索