Node.js
- Node.js 就是運行在服務端的 JavaScript熬粗。
- Node.js 是一個基于Chrome JavaScript 運行時建立的一個平臺。
- Node.js是一個事件驅(qū)動I/O服務端JavaScript環(huán)境余境,基于Google的V8引擎驻呐,V8是Javascript代碼解析器,V8引擎執(zhí)行Javascript的速度非撤祭矗快含末,性能非常好。
異步式I/O(非阻塞式(Asynchronous)I/O)
針對所有I/O操作不采用阻塞策略即舌,當線程遇到I/O操作時佣盒,不會以阻塞的方式等待I/O操作的完成或數(shù)據(jù)的返回,而只是將IO請求發(fā)送給操作系統(tǒng)顽聂,繼續(xù)執(zhí)行下一條語句肥惭,當操作系統(tǒng)完成IO操作時盯仪,以事件的形式通知執(zhí)行IO操作的線程,線程會在特定時候處理這個事件蜜葱,為了 處理異步IO脐供,線程必須有事件循環(huán)于样,不斷的檢查有沒有未處理的事件蚣抗,依次予以處理寡壮。
http://www.phperz.com/special/70.html node.js 資料
Node.js 應用是由哪幾部分組成的:
1.引入 required 模塊:我們可以使用 require 指令來載入 Node.js 模塊糊昙。
2.創(chuàng)建服務器:服務器可以監(jiān)聽客戶端的請求寒瓦,類似于 Apache 逛拱、Nginx 等 HTTP 服務器邻遏。
3.接收請求與響應請求 服務器很容易創(chuàng)建诗良,客戶端可以使用瀏覽器或終端發(fā)送 HTTP 請求汹桦,服務器接收請求后返回響應數(shù)據(jù)。
Package.json 屬性說明
name - 包名鉴裹。
version - 包的版本號舞骆。
description - 包的描述。
homepage - 包的官網(wǎng) url 径荔。
author - 包的作者姓名督禽。
contributors - 包的其他貢獻者姓名。
dependencies - 依賴包列表总处。如果依賴包沒有安裝狈惫,npm 會自動將依賴包安裝在 node_module 目錄下。
repository - 包代碼存放的地方的類型鹦马,可以是 git 或 svn胧谈,git 可在 Github 上。
main - main 字段指定了程序的主入口文件荸频,require('moduleName') 就會加載這個文件菱肖。這個字段的默認值是模塊根目錄下面的 index.js。
keywords - 關鍵字
Node.js REPL(Read Eval Print Loop:交互式解釋器) 表示一個電腦的環(huán)境旭从,類似 Window 系統(tǒng)的終端或 Unix/Linux shell稳强,我們可以在終端中輸入命令,并接收系統(tǒng)的響應和悦。
URL
url 模塊提供了一些實用函數(shù)退疫,用于 URL 處理與解析。 http://nodejs.cn/api/url.html
const url = require('url');
一個 URL 字符串是一個結構化的字符串鸽素,它包含多個有意義的組成部分褒繁。 當被解析時,會返回一個 URL 對象付鹿,它包含每個組成部分作為屬性澜汤。
通過Node.js提供的API解析一個URL:
const url = require('url');
const myURL = url.parse('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');
url.hash:獲取及設置URL的分段(hash)部分蚜迅。
const myURL = new URL('https://example.org/foo#bar');
console.log(myURL.hash); // 輸出 #bar
url.host:獲取及設置URL的主機(host)部分。
const myURL = new URL('https://example.org:81/foo');
console.log(myURL.host); // 輸出 example.org:81
myURL.host = 'example.com:82';
console.log(myURL.href); // 輸出 https://example.com:82/foo
url.hostname:獲取及設置URL的主機名(hostname)部分俊抵。 url.host和url.hostname之間的區(qū)別是url.hostname不 包含端口谁不。
url.port:獲取及設置URL的端口(port)部分。完全無效的端口字符串將被忽略徽诲。如果字符串以數(shù)字開頭刹帕,那么開頭部位的數(shù)字將會被賦值給port。數(shù)字范圍0~65535(包括)谎替。
url.href:獲取及設置序列化的URL偷溺。獲取href屬性的值等同于調(diào)用url.toString()。
url.protocol:獲取及設置URL的協(xié)議(protocol)部分钱贯。
url.origin:獲取只讀序列化的URL origin部分挫掏。
const myURL = new URL('https://example.org/foo/bar?baz');
console.log(myURL.origin); // 輸出 https://example.org
url.pathname:獲取及設置URL的路徑(path)部分。
const myURL = new URL('https://example.org/abc/xyz?123');
console.log(myURL.pathname); // 輸出 /abc/xyz
url.search:獲取及設置URL的序列化查詢(query)部分部分秩命。
const myURL = new URL('https://example.org/abc?123');
console.log(myURL.search); // 輸出 ?123 問號后面的那部分
url.username:獲取及設置URL的用戶名(username)部分尉共。
const myURL = new URL('https://abc:xyz@example.com');
console.log(myURL.username); // 輸出 abc
console.log(myURL.password); // 輸出 xyz
url.toString():在URL對象上調(diào)用toString()方法將返回序列化的URL。
url.toJSON():在URL對象上調(diào)用toJSON()方法將返回序列化的URL弃锐。
當URL對象使用JSON.stringify()序列化時將自動調(diào)用該方法袄友。
const myURLs = [
new URL('https://www.example.com'),
new URL('https://test.example.org')
];
console.log(JSON.stringify(myURLs));
// 輸出 ["https://www.example.com/","https://test.example.org/"]
url.format(URL[, options]) 方法允許輸出的基本自定義。
const myURL = new URL('https://a:b@你好你好?abc#foo');
console.log(myURL.toString());
// 輸出 https://a:b@xn--6qqa088eba/?abc#foo
console.log(url.format(myURL, { fragment: false, unicode: true, auth: false }));
// 輸出 'https://你好你好/?abc'
auth:如果序列化的URL字符串應該包含用戶名和密碼為true霹菊,否則為false剧蚣。默認為true
fragment:如果序列化的URL字符串應該包含分段為true,否則為false旋廷。默認true
search:如果序列化的URL字符串應該包含搜索查詢?yōu)閠rue鸠按,否則為false。默認true
unicode:如果出現(xiàn)在URL字符串主機元素里的Unicode字符應該被直接編碼為true
url.format(urlObject) urlObject一個 URL 對象(就像 url.parse() 返回的)柳洋。 如果是一個字符串待诅,則通過 url.parse() 轉(zhuǎn)換為一個對象。
url.format() 方法返回一個從 urlObject 格式化后的 URL 字符串熊镣。
url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
urlString 為要解析的 URL 字符串卑雁。
url.parse() 方法會解析一個 URL 字符串并返回一個 URL 對象。
HTTP
Node.js 中的 HTTP 接口被設計成支持協(xié)議的許多特性绪囱。這些接口不緩沖完整的請求或響應测蹲,用戶能夠以流的形式處理數(shù)據(jù)。
HTTP 消息頭由一個對象表示,鍵名是小寫的鬼吵,鍵值不能修改扣甲。例如:
{ 'content-length': '123',
'content-type': 'text/plain',
'connection': 'keep-alive',
'host': 'mysite.com',
'accept': '*/*' }
HTTP API 是非常底層的。 它只涉及流處理與消息解析。它把一個消息解析成消息頭和消息主體琉挖,但不解析具體的消息頭或消息主體启泣。
http.Agent 類
Agent 負責為 HTTP 客戶端管理連接的持續(xù)與復用。 它為一個給定的主機與端口維護著一個等待請求的隊列示辈,且為每個請求重復使用一個單一的 socket 連接直到隊列為空寥茫,此時 socket 會被銷毀或被放入一個連接池中,在連接池中等待被有著相同主機與端口的請求再次使用矾麻。 是否被銷毀或被放入連接池取決于KeepAlive選項纱耻。
var http=require("http");
http.createServer(function(req,res){
res.writeHead(200,{
"content-type":"text/plain"
});
res.write("hello world");
res.end();
}).listen(3000);
我們首先用http.createServer函數(shù)創(chuàng)建了一個服務器對象,然后調(diào)用了response.writeHead方法:該方法的第一個參數(shù)表示HTTP的響應狀態(tài)(200)表示一切正常险耀;第二個參數(shù)是“Content-Type”弄喘,表示我響應給客戶端的內(nèi)容類型。然再后我們調(diào)用了response.write方法甩牺,寫入我們需要傳遞給客戶端的內(nèi)容蘑志。最后一步我們調(diào)用了response.end,表示此次請求已處理完成柴灯。
httpService (http服務器)
開篇的實例代碼卖漫,也可以通過如下的代碼進行改寫一番:
var http=require("http");
var server=new http.Server();
server.on("request",function(req,res){
res.writeHead(200,{
"content-type":"text/plain"
});
res.write("hello nodejs");
res.end();
});
server.listen(3000);
以上代碼是通過直接創(chuàng)建一個http.Server
對象,然后為其添加request
事件監(jiān)聽赠群,其實也就說createServer
方法其實本質(zhì)上也是為http.Server
對象添加了一個request
事件監(jiān)聽,這似乎更好理解了旱幼,那讓我們看看http的重要屬性:
createServer
方法中的參數(shù)函數(shù)中的兩個參數(shù)req和res則是分別代表了請求對象和響應對象查描。其中req是http.IncomingMessage
的實例,res是http.ServerResponse
的實例柏卤。
- http.IncomingMessage
http.IncomingMessage是HTTP請求的信息冬三,是后端開發(fā)者最關注的內(nèi)容,一般由http.Server的request事件發(fā)送缘缚,并作為第一個參數(shù)傳遞勾笆,包含三個事件- data:當請求體數(shù)據(jù)到來時,該事件被觸發(fā)桥滨,該事件提供一個參數(shù)chunk窝爪,表示接受的數(shù)據(jù),如果該事件沒有被監(jiān)聽齐媒,則請求體會被拋棄蒲每,該事件可能會被調(diào)用多次(這與nodejs是異步的有關系)
- end:當請求體數(shù)據(jù)傳輸完畢時,該事件會被觸發(fā)喻括,此后不會再有數(shù)據(jù)
- close:用戶當前請求結束時邀杏,該事件被觸發(fā),不同于end唬血,如果用戶強制終止了傳輸望蜡,也是用close
可以參考另一篇文章《nodejs + cheerio 爬取極客學院的nodejs課程數(shù)據(jù)》來了解http模塊在爬蟲中的簡單應用唤崭。
- http.ServerResponse
http.ServerResponse是返回給客戶端的信息,決定了用戶最終看到的內(nèi)容脖律,一般也由http.Server的request事件發(fā)送,并作為第二個參數(shù)傳遞状您,它有三個重要的成員函數(shù),用于返回響應頭膏孟、響應內(nèi)容以及結束請求- res.writeHead(statusCode,[heasers]):向請求的客戶端發(fā)送響應頭,該函數(shù)在一個請求中最多調(diào)用一次柒桑,如果不調(diào)用弊决,則會自動生成一個響應頭
- res.write(data,[encoding]):想請求的客戶端發(fā)送相應內(nèi)容,data是一個buffer或者字符串魁淳,如果data是字符串飘诗,則需要制定編碼方式,默認為utf-8界逛,在res.end調(diào)用之前可以多次調(diào)用
- res.end([data],[encoding]):結束響應昆稿,告知客戶端所有發(fā)送已經(jīng)結束,當所有要返回的內(nèi)容發(fā)送完畢時息拜,該函數(shù)必需被調(diào)用一次溉潭,兩個可選參數(shù)與res.write()相同。如果不調(diào)用這個函數(shù)少欺,客戶端將用于處于等待狀態(tài)喳瓣。
http client
http模塊提供了兩個函數(shù) http.request
和 http.get
,功能是作為客戶端向http服務器發(fā)起請求赞别。
-
http.request(options,callback)
options是一個類似關聯(lián)數(shù)組的對象畏陕,表示請求的參數(shù),callback作為回調(diào)函數(shù)仿滔,需要傳遞一個參數(shù)惠毁,為http.ClientResponse的實例,http.request返回一個http.ClientRequest的實例堤撵。
options常用的參數(shù)有host仁讨、port(默認為80)、method(默認為GET)实昨、path(請求的相對于根的路徑洞豁,默認是“/”,其中querystring應該包含在其中,例如/search?query=byvoid)丈挟、headers(請求頭內(nèi)容)var http=require("http"); var options={ hostname:"cn.bing.com", port:8080 } var req=http.request(options,function(res){ res.setEncoding("utf-8"); res.on("data",function(chunk){ console.log(chunk.toString()) }); console.log(res.statusCode); }); req.on("error",function(err){ console.log(err.message); }); req.end();
發(fā)送POST請求(模擬了向慕課網(wǎng)發(fā)起評論的功能刁卜,headers請使用開發(fā)者工具從請求中獲取,基本上是參考scott老師的代碼)
var http=require("http"); var querystring=require("querystring"); var postData=querystring.stringify({ "content":"just a test", "mid":8837 }); var options={ hostname:"www.imooc.com", port:80, path:"/course/document", method:"POST", headers:{ "Accept":"application/json, text/javascript, */*; q=0.01", "Accept-Encoding":"gzip, deflate", "Accept-Language":"zh-CN,zh;q=0.8", "Connection":"keep-alive", "Content-Length":postData.length, "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8", "Cookie":"imooc_uuid=6cc9e8d5-424a-4861-9f7d-9cbcfbe4c6ae; imooc_isnew_ct=1460873157; loginstate=1; apsid=IzZDJiMGU0OTMyNTE0ZGFhZDAzZDNhZTAyZDg2ZmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjkyOTk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNmNmFhMmVhMTYwNzRmMjczNjdmZWUyNDg1ZTZkMGM1BwhXVwcIV1c%3DMD; PHPSESSID=thh4bfrl1t7qre9tr56m32tbv0; Hm_lvt_f0cfcccd7b1393990c78efdeebff3968=1467635471,1467653719,1467654690,1467654957; Hm_lpvt_f0cfcccd7b1393990c78efdeebff3968=1467655022; imooc_isnew=2; cvde=577a9e57ce250-34", "Host":"www.imooc.com", "Origin":"http://www.imooc.com", "Referer":"http://www.imooc.com/video/8837", "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2763.0 Safari/537.36", "X-Requested-With":"XMLHttpRequest", } } var req=http.request(options,function(res){ res.on("data",function(chunk){ console.log(chunk); }); res.on("end",function(){ console.log("### end ##"); }); console.log(res.statusCode); }); req.on("error",function(err){ console.log(err.message); }) req.write(postData); req.end();
http.get(options,callback)
這個方法是http.request方法的簡化版,唯一的區(qū)別是http.get自動將請求方法設為了GET請求曙咽,同時不需要手動調(diào)用req.end()蛔趴,但是需要記住的是例朱,如果我們使用http.request方法時沒有調(diào)用end方法洒嗤,服務器將不會收到信息渔隶。
request
可以將requset模塊想象成一個簡化版的第三方類http模塊绞灼,同時支持https 和重定向低矮,戳這里區(qū)官網(wǎng)商佛。下文列出幾個能夠讓你快速上手的知識點。
安裝
npm install request --save
var request = require('request');
API
-
GET
request(url,function(error,response,body){ if(!error && response.statusCode == 200){ //輸出返回的內(nèi)容 console.log(body); } });
-
POST
var options = { uri: 'https://www.googleapis.com/urlshortener/v1/url', method: 'POST', json: { "longUrl": "http://www.google.com/" } }; request({ url: 'http://xxx.xxx.com', method: 'POST', body: formData }, function(error, response, body) { if (!error && response.statusCode == 200) { //輸出返回的內(nèi)容 console.log(body); } });
流
任何響應都可以輸出到文件流幔戏。
request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))
反過來闲延,也可以將文件傳給PUT或POST請求。未提供header的情況下找颓,會檢測文件后綴名,在PUT請求中設置相應的content-type益老。
fs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json'))
表單
request支持 application/x-www-form-urlencoded
和 multipart/form-data
實現(xiàn)表單上傳捺萌。
-
x-www-form-urlencoded:
request.post('http://service.com/upload', {form:{key:'value'}}) 或者: request.post('http://service.com/upload').form({key:'value'})
-
multipart/form-data
var r = request.post('http://service.com/upload') var form = r.form() form.append('my_field', 'my_value') form.append('my_buffer', new Buffer([1, 2, 3])) form.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png')) form.append('remote_file', request('http://google.com/doodle.png'))
superagent
superagent它是一個強大并且可讀性很好的輕量級ajax API桃纯,是適用于nodejs環(huán)境的一個關于HTTP方面的庫态坦。
安裝
npm install superagent --save
簡單使用
一個請求的初始化可以用請求對象里合適的方法來執(zhí)行驮配,然后調(diào)用end()來發(fā)送請求壮锻。
var superagent = require('superagent');
superagent
.post('/api')
.send({
'key': 'value'
})
.set('header_key', 'header_value')
.end(function(err, res) {
if (err) {
//do something
} else {
//do something
}
})
或
superagetn
.get(''http://example.com/search'')
.end(function(res){ });
API
請求方法的參數(shù)可以直接使用多個key/value猜绣,也可以分多次調(diào)用請求方法每次傳遞一對key/value或者key/value字符串
-
GET
//接下來四種方法所形成的URL為/api?name=An&age=20&sex=male //第一種 superagent .get(/api) .query({name:'liang'}) .query({age:18}) .query({sex:'female'}) .end(function(res){ }) //第二種 superagent .get(/api) .query({name:'liang',age:18,sex:'female'}) .end(function(res){ }) //第三種 superagent .get(/api) .query('name=liang&age=18&sex=female') .end(function(res){ }) //第四種 superagent .get(/api) .query('name=liang') .query('age=18') .query('sex=female') .end(function(res){ })
-
POST
superagent .post('/api') .set('Content-Type','application/json') .send('{"name":"An","age":20,"sex":"male"}') .end(cb) //等價于 下面的寫法,因為json是默認的 Content-Type superagent .post('/api') .send({name:"An",age:20,sex:"male"}) .end(cb) //等價于 ==> superagent .post('/api') .send({name:"An"}) .send({age:20}) .sex({sex:'male'}) .end(cb)
superagent的請求數(shù)據(jù)格式化是可以擴展的辣之,不過默認支持form和json兩種格式,想發(fā)送數(shù)據(jù)以application/x-www-form-urlencoded類型的話怀估,則可以簡單的調(diào)用.type()方法傳遞form參數(shù)就行多搀,這里默認是json,下面的請求將會發(fā)送post name=a&age=18:
request .post('/user') .type('form') .send({ name: 'tj' }) .send({ pet: 'tobi' }) .end(callback)
post && get
當用.send(obj)方法來發(fā)送一個post請求康铭,并且希望傳遞一些查詢字符串从藤,可以調(diào)用.query()方法,比如向?format=json&dest=/login發(fā)送post請求:
request
.post('/')
.query({ format: 'json' })
.query({ dest: '/login' })
.send({ post: 'data', msg: 'hello' })
.end(callback);
請求設置
- 設置請求頭:調(diào)用set()方法呛哟,參數(shù)傳遞一組鍵值對
superagent .get('/api') .set({ 'Referer','https://www.google.com', 'Accept','image/webp,image/*,*/*;q=0.8' }) .end(function(req,res){ //do something })
- Response
響應一般會提供很多有用的標識以及屬性,都在response對象里扫责,按照respone.text,解析后的response.body,頭字段鳖孤,一些標識的順序來排列苏揣。
- res.text
包含未被解析的響應數(shù)據(jù)
- res.body
包含解析的數(shù)據(jù)平匈,跟請求數(shù)據(jù)自動序列化一樣忍燥,響應數(shù)據(jù)也會自動的解析隙姿,
當為一個Content-Type队丝。定義一個解析器后欲鹏,就能自動解析赔嚎,默認解析包
含application/json和application/x-www-form-urlencoded,可以
通過訪問res.body來訪問解析對象尽狠。
- res.header
響應頭,res.header包含解析之后的響應頭數(shù)據(jù),鍵值都是node處理成小
寫字母形式袄膏,比如res.header['content-length'].
- res.type & res.charset 類型和編碼格式
Content-Type響應頭字段是一個特列沉馆,服務器提供res.type來訪問它,
默認res.charset是空的,如果有的話揖盘,則自動填充兽狭,例如Content-Type
值為text/html; charset=utf8,則res.type為text/html,res.charst
為utf8.
- res.status狀態(tài)碼
其他設置
req.abort() 終止請求
req.timeout(ms) 暫停請求 ms 表示毫秒為單位的時間
-
管道數(shù)據(jù)
nodejs客戶端允許使用一個請求流來輸送數(shù)據(jù),比如請求一個文件作為輸出流:var request = require('superagent') ,fs = require('fs'); var stream = fs.createReadStream('path/to/my.json'); var req = request.post('/somewhere'); req.type('json'); stream.pipe(req);
或者輸送一個響應流到文件中:
var request = require('superagent') , fs = require('fs'); var stream = fs.createWriteStream('path/to/my.json'); var req = request.get('/some.json'); req.pipe(stream);
-
錯誤處理
當發(fā)送錯誤時,superagent首先會檢查回調(diào)函數(shù)的參數(shù)數(shù)量,當err參數(shù)提供的話颠焦,參數(shù)就是兩個,如下:request .post('/upload') .attach('image', 'path/to/tobi.png') .end(function(err, res){ }); `` 當省略了回調(diào)函數(shù),或者回調(diào)只有一個參數(shù)的話,可以添加error事件的處理. ```js request .post('/upload') .attach('image', 'path/to/tobi.png') .on('error', handle) .end(function(res){ });
RESTful API
支持Web服務和動態(tài)Web應用程序的多層架構伐庭,實現(xiàn)可重用性、可擴展性盯捌、組件可響應性的清晰分離蘑秽。開發(fā)人員可輕松使用AJAX和RESTful Web服務創(chuàng)建豐富網(wǎng)絡應用幼衰。
單線程
Node.js單線程是指Node并沒有創(chuàng)建線程的能力渡嚣,所以代碼都是單線程執(zhí)行的识椰。不過Node宿主環(huán)境并不是單線程的腹鹉,它維護一個執(zhí)行隊列功咒,循環(huán)檢測并調(diào)度JS線程來執(zhí)行力奋,因此單線程執(zhí)行和并發(fā)操作并不沖突。
事件輪詢機制
Node.js 可在不斷新增額外線程的情況下溅呢,依然對任務進行并發(fā)處理藕届。它是通過事件輪詢(event loop)來實現(xiàn)并行操作的休偶。
非堵塞I/O
由于Node.js是事件驅(qū)動的踏兜,因此使用了事件循環(huán)來解決I/O操作帶來的瓶頸。在Node.js中一個I/O操作通常會帶有 一個回調(diào)函數(shù)八秃,當I/O操作完畢并返回時碱妆,會調(diào)用此回調(diào)函數(shù)。與此同時昔驱,主線程則繼續(xù)執(zhí)行接下來的代碼疹尾。
V8虛擬機
Node.js是一個基于Google Chrome V8 Javascript引擎之上的平臺,可用于創(chuàng)建輕量級骤肛、快速纳本、可擴展、事件驅(qū)動和非堵塞I/O的應用腋颠。
事件驅(qū)動
Node.js使用事件驅(qū)動模型繁成,即當Web Server接收到請求時絮蒿,將其關閉然后進行處理,然后去服務下一個Web請求。當請求完成后,被放回處理隊列中您没。當?shù)竭_隊列開頭時,結果被返回給客戶端。
node中實現(xiàn)IPC通道的是管道技術(pipe)莺匠。在node中管道是個抽象層面的稱呼期贫,具體細節(jié)實現(xiàn)由libuv提供迹冤,在win下是命名管道(named pipe)實現(xiàn)堪藐,在*nix下模捂,采用unix Domain Socket來實現(xiàn)。