Node 模塊原理與用法詳解
本文實例講述了Node 模塊原理與用法。分享給大家供大家參考,具體如下:
簡介
V8引擎本身就是用于Chrome瀏覽器的JS解釋部分,但是Ryan Dahl,把V8搬到服務(wù)器,用于做服務(wù)器的軟件。
Node是一個專注于實現(xiàn)高性能Web服務(wù)器優(yōu)化的專家,在遇到V8而誕生的項目
- 沒有歷史包袱,沒有同步I/O。不會出現(xiàn)一個同步I/O導致事件循環(huán)性能急劇降低的情況。
- V8性能足夠好,遠遠比Python,Ruby等其它腳本語言的引擎快。
- JavaScript語言的閉包特性非常方便,比C中的回調(diào)函數(shù)好用。
Node可以讓JavaScript運行在服務(wù)器端的平臺開發(fā),它讓JavaScript的觸角延伸到了服務(wù)器端,可以與PHP,JSP,Python,Ruby等語言實現(xiàn)后端開發(fā)。
但Node似乎有點不同:
- Node不是一種獨立的語言,與PHP,JSP,Python,Perl,Ruby的“即使語言,也是平臺”不同,Node使用的是JavaScript進行編程,運行在JavaScript引擎上(V8)
- 與PHP,JSP等相比(PHP,JSP,.net都需要運行在服務(wù)器程序上,Apache,Naginx,Tomcat,IIS),Node跳過了Apcahe,Naginx,IIS等HTTP服務(wù)器,它自己不用建設(shè)在任何服務(wù)器任何之上。Node的設(shè)計理念與經(jīng)典架構(gòu)(LAMP = Linux + Apache + MySQL + PHP) 有著很大的不同,可以提供強大的伸縮能力。
- Node沒有Web容器。
- Node是花最小的硬件成本,追求更高的并發(fā),更高的處理性能。
Node特點
所謂特點,就是Node如果解決服務(wù)器高性能瓶頸問題。
JavaScript有什么特點的時候,會立即想到 單線程,事件驅(qū)動, 面向?qū)ο?/code>。但是JavaScript精髓 覺得是 this, 閉包 ,作用域鏈, 函數(shù)。才使得這門語言魅力無窮。
單線程
在Java,PHP,或者.net 等服務(wù)器端語言中,會為每一個用戶端連接創(chuàng)建一個新的線程。而每個線程需要耗費大約2MB內(nèi)存。理論上,一個8GB內(nèi)存的服務(wù)器可以同時連接的最大用戶數(shù)4000個左右。要讓Web應(yīng)用程序支持更多的用戶,就需要增加服務(wù)器的數(shù)量,而Web應(yīng)用程序的硬件成本就上升了。
Node不為每個用戶連接創(chuàng)建一個新的線程,而僅僅使用一個線程。當有用戶連接了,就觸發(fā)一個內(nèi)部事件,并通過非阻塞I/O,事件驅(qū)動機制,讓Node程序宏觀上也是并行的。Node中,一個8GB內(nèi)存的服務(wù)器,可以同時處理超過4萬用戶的連接。
單線程好處:操作系統(tǒng)完全不再有線程創(chuàng)建,銷毀的時間開銷。
單線程壞處:就是一個用戶造成了線程的奔潰,整個服務(wù)都奔潰了,其它人的服務(wù)也就奔潰了。


單線程也能夠造成宏觀上的“并發(fā)”。
非阻塞I/O
非阻塞I/O non-blocking I/O
例子:訪問數(shù)據(jù)庫取得數(shù)據(jù)的時候,需要一段時間。
在傳統(tǒng)的單線程處理機制中,在執(zhí)行了訪問數(shù)據(jù)庫代碼之后,整個線程都將暫停下來,等待數(shù)據(jù)庫返回結(jié)果,才能執(zhí)行后面的代碼。也就是說I/O阻塞了代碼的執(zhí)行,極大降低了程序執(zhí)行的效率。
Node采用了非阻塞型I/O機制,因此在執(zhí)行了訪問數(shù)據(jù)庫的代碼之后,將立即轉(zhuǎn)而執(zhí)行后面的代碼,把數(shù)據(jù)庫返回的結(jié)果的處理代碼放在回調(diào)函數(shù)中,從而提高了程序的執(zhí)行效率。
當某個I/O執(zhí)行完畢時,將以時間的形式通知執(zhí)行I/O操作的線程,線程執(zhí)行了這個事件的回調(diào)函數(shù)。為了處理異步I/O,線程必須有事件循環(huán),不斷的檢查是否有未處理的時間。依次予以處理。
阻塞模式下,一個線程只能處理一項任務(wù),要想提高吞吐量必須通過多線程。而非阻塞模式下,一個線程永遠在執(zhí)行計算操作,這個線程的CPU核心利用率永遠是100%。 有一種類似 : 與其多人工作,但是好多人閑著,倒不如一個人工作,往死里干活。

事件驅(qū)動
事件驅(qū)動 event-driven
在Node中,客戶端請求建立連接,提交數(shù)據(jù)等行為,會觸發(fā)相應(yīng)的時間。在Node中,在一個ie時時刻,只能執(zhí)行一個事件回調(diào)函數(shù),但是在執(zhí)行一個事件回調(diào)函數(shù)的中途,可以轉(zhuǎn)而處理其它事件(比如:有新用戶連接),然后返回繼續(xù)執(zhí)行原事件的回調(diào)函數(shù)。這種處理機制,稱為:"事件環(huán)"機制。
Node底層是C++(V8也是C++) 編寫。底層代碼中,近半數(shù)都用戶事件隊列,回調(diào)函數(shù)隊列的構(gòu)建。用事件驅(qū)動來完成服務(wù)器的任務(wù)調(diào)度。用一個線程,擔負起了處理非常多的任務(wù)。

單線程,減少內(nèi)存開銷,操作系統(tǒng)的內(nèi)存換頁。
如某一個任務(wù),執(zhí)行了,但是被I/O阻塞了,所以這個縣城就阻塞了。非阻塞I/O,不會傻等I/O語句結(jié)束,而會執(zhí)行后面的語句。利用事件驅(qū)動,不管是新用戶的請求,還是老用戶的I/O完成,都將以事件方式加入事件環(huán)中,等待調(diào)度。
Node所有的I/O都是異步的,回調(diào)函數(shù)嵌套回調(diào)函數(shù)。
Node是單進程單線程應(yīng)用程序,但是通過事件和回調(diào)支持并發(fā),所以性能非常高。
Node的每個API都是異步的,并作為一個獨立線程運行,使用異步函數(shù)調(diào)用,并處理并發(fā)。
Node基本上所有的事件機制都是用設(shè)計模式中的觀察者模式實現(xiàn)的。
Node單線程類似進入一個while(true)的事件循環(huán),直到?jīng)]有事件觀察者退出,每個異步事件都生成一個事件觀察者,如果有事件發(fā)生就調(diào)用該回調(diào)函數(shù)。
模塊
moduel
Node中,以模塊為單位劃分所有功能,并且提供一個完整的模塊加載機制,可以將應(yīng)用程序話費為各個不同的部分。
Node中,一個JavaScript文件中定義的變量,函數(shù),都只在這個文件內(nèi)部有效果。
俠義的說,每一個JavaScript文件都是一個模塊,而多個JavaScript文件之間可以相互require,共同實現(xiàn)一個功能,整體外對,又稱之為廣義上的模塊
好處:
- 減少重復代碼量,增加可讀性。
- 方便進行代碼規(guī)劃。
- 方面使用第三方模塊。
當需要從JS文件外部引用到這些變量,函數(shù)時,必須使用exprots對象,或者module.exprots進行暴露。使用者需要使用require(); 函數(shù)引入這個JS文件。
function People( name,sex,age ){
this.name = name;
this.sex = sex;
this.age = age;
}
People.prototype = {
sayHello: function(){
console.log(this.name+this.sex+this.age);
}
};
// 暴露
module.exports = People;
// 使用
var People = require('./People.js');
var p1 = new People('zf','nv','23');
p1.sayHello();
一個JavaScript文件,可以向外exprots無數(shù)個變量,函數(shù),對象,但是require(); 的時候,僅僅需要 載入一次JS文件即可。 所以,無形之后,會增加一個頂層命名空間。
// 變量
// 需要變量引用 使用
exports.a = 10;
// 直接需要變量值使用.
module.exports = name;
// 對象
module.exports = {
name1: 123,
name2: 456
}
// 暴露結(jié)果: { name1: 123, name2: 456 }
exports.msg = {
name1: 1,
name2: 2
}
// 暴露結(jié)果 : { msg: { name1: 1, name2: 2 } }
// 函數(shù)
exports.showMsg = function () {
}
// 暴露結(jié)果 : { showMsg: [Function] }
// 在 引用結(jié)果 需要 通過 變量 引用對象 執(zhí)行
// var msg = require();
// msg.showMsg();
module.exports = function () {
}
// 暴露結(jié)果 [Function]
// 引入文件的 變量 直接執(zhí)行
模板引擎
數(shù)據(jù)綁定,成為一個完整的HTML字符串。
Node中使用的模板:ejs 和 jade
后臺模板引擎:
<ul>
<% for(var i = 0 ; i < news.length ; i++){ %>
<li><%= news[i] %></li>
<% } %>
</ul>
// 模板中需要的數(shù)據(jù)
var dictionary = {
a:6,
news : ["xixi","haha"]
};
HTTP模塊
主要類
Class: http.Server
var server = http.createServer();
server就是http.Server類的實例。
常用的方法有:
server.listen(port, [hostname], [backlog], [callback])
Class: http.ServerResponse
var server = http.createServer(function(req,res){ });
res就是 http.ServerResponse類的實例。
Class: http.IncomingMessage
``
var server = http.createServer(function(req,res){ });
``
req就是http.IncomingMessage類的實例。
server對象
可以使用on語法監(jiān)聽某個事件。
var http = require('http');
var server = http.createServer();
server.on('request',function ( req,res ) {
res.setHeader('Content-type','text/html;charset=utf8');
if ( req.url == '/' ){
res.end('index');
} else {
res.end('404');
}
});
server.listen(8080,'localhost');
req對象
每次上行請求頭對象
req.headers //HTTP上行請求頭

req.httpVersion // http請求的版本?,F(xiàn)在基本上都是1.1 req.method // “GET”、”POST”就是請求的類型 req.url // 用戶請求的網(wǎng)址
res對象
每次下行響應(yīng)對象
res.end() // 每次都要有這個語句,表示這次的發(fā)送已經(jīng)結(jié)束
// 參數(shù)必須是string、buffer(圖片、文件)。
res.write() // 就是寫HTTP下行請求的body
res.setHeader() // 設(shè)置返回的報文頭部
res.statusCode = 404; // 設(shè)置狀態(tài)碼
res.writeHead() // 和res.setHeader差不多
res.writeHead(288 , {"Content-Type":"text/plain"});
url模塊
作用內(nèi)置模塊,解析url,解析地址。 分析和解析 URL 的工具
url.parse()
url.parse()就是用來解析網(wǎng)址,接收一個字符串,返回一個JSON:
var obj = url.parse("http://localhost:8080/a/b/c/1.html?name=ting&age=21");

url.parse方法第二個參數(shù)如果是true,那么返回的對象中的query就是json
query: { xingming: 'xiaoming', age: '12' }
querystring模塊
querystring模塊是專門用來解析GET請求的查詢字符串的。
console.log( qs.parse('name=ting&age=21&hobby=run&hobby=sing') );
// 返回:{ name: 'ting', age: '21', hobby: [ 'run', 'sing' ] }
path模塊
處理和轉(zhuǎn)換文件路徑的工具集,專門處理路徑
path.basename() 返回路徑中的文件名
path.dirname() 返回路徑中的文件夾名
path.extname() 返回路徑的拓展名
console.log( path.basename('/xixi/haha/a.html') ); //a.html
console.log( path.extname('/xixi/haha/a.html') ); //.html
console.log( path.dirname('/xixi/haha/a.html') ); ///xixi/haha
fs模塊
文件處理模塊,可以讀文件,也可以寫文件
fs.readFile(); //讀取文件內(nèi)容 fs.readDir(); //讀取文件夾名 fs.appendFile(); //追加文件 fs.writeFile(); //寫入文件(覆蓋原有的) fs.rename(); //修改文件名
自定義模塊
每一個js文件中可以看成是一個小小的模塊
require()誰,就會執(zhí)行誰。就相當于調(diào)用一個函數(shù)。A require B, 先執(zhí)行B全部語句,然后執(zhí)行A的剩余語句。
require('./test/a.js');
每個js文件就是一個閉包,聲明的函數(shù)、變量只在這個js文件內(nèi)部有定義。
A require了 B , 那么B里面的所有路徑都要按照A的路徑寫。
如果需要使用到其它文件中的變量,使用exports暴露出去。
exports.*** = ***; testA .js var a = 100; exports.a = a;
主文件
var testA = requrie('./testA.js');
console.log( testA.a );
暴露唯一的接口,module.exports ,一般使用到構(gòu)造函數(shù)。
如果只有寫文件載入,會去默認文件夾下:node_modules 尋找是否有當前需要載入的文件
require('test.js');
也可以直接省略路徑、省略文件名,只寫文件夾名
require('aa');
實際上引用的是node_moduels文件夾里面的aa文件夾里面的index.js文件。
一般第三方模塊,都放入node_modules文件夾中。
package.json
配置信息:
{
"name": "my_package", //項目名字
"version": "1.0.0", //版本號
"main": "index.js", //入口文件
"keywords": [], //關(guān)鍵詞,就是搜索什么npm上能夠顯示你
"author": "ag_dubs", //作者
"license": "ISC", //版權(quán)協(xié)議
"repository": { //代碼托管倉庫,這個會自動生成一個連接
"type": "git",
"url": "https://github.com/ashleygwilliams/my_package.git"
},
"bugs": { //如果發(fā)現(xiàn)bug應(yīng)該交給誰
"url": "https://github.com/ashleygwilliams/my_package/issues"
},
"dependencies": {
"underscore": "*",
"date-format" : "0.0.2"
},
"homepage": "https://github.com/ashleygwilliams/my_package" //個人網(wǎng)站
}
最重要的信息是:依賴
{
"dependencies": {
"underscore": "*",
"date-format" : "^0.0.2"
}
}
formidable
處理POST請求
// formidable 語法
var form = new formidable.IncomingForm();
// 設(shè)置上傳路徑
form.uploadDir = "./uploads";
form.parse(req, function(err, fields, files) {
// fields是普通域,就是普通的文本框、單選按鈕、復選按鈕、textarea都存在這個對象里面
// files是上傳的文件信息
var newname = df('yyyyMMddhhmmssSSS', new Date());
fs.rename(files.touxiang.path , "./uploads/" + newname + ".jpg",function(err){
if(err){
res.end("error");
return ;
}
});
res.end("ok");
});
爬蟲初級
需要的npm包:
- express
- request (后端發(fā)送請求的模塊)
- cheerio (像前端一樣操作拉取回來的數(shù)據(jù))
爬蟲以及Robots協(xié)議介紹:
- 爬蟲,是中自動獲取網(wǎng)頁內(nèi)容的程序。是搜索引擎的重要組成部分,因此搜索引擎優(yōu)化很大程度上就是針對爬蟲而做出的優(yōu)化。
- robots.txt是一個文本文件,robots.txt是一個協(xié)議,不是一個命令。robots.txt是爬蟲要查看的第一個文件。robots.txt文件告訴爬蟲在服務(wù)器上什么文件是可以被查看的,搜索機器人就會按照該文件中的內(nèi)容來確定訪問的范圍。

var express = require('express');
var app = express();
var cheerio = require('cheerio');
app.get('/', function(req, res){
var request = require('request');
request('https://linxingzhang.com', function(err, response, body) {
if (!err && response.statusCode == 200) {
$ = cheerio.load(body); // 和jquery的$('body') 一樣
res.json({
panel: $('#link-panel li').length
});
}
});
});
app.listen(3000);
使用supervisor啟動
> supervisor start app.js
常用npm包
| 模塊名 | 鏈接地址 | 簡介 |
|---|---|---|
| async | async | 異步操作管理 |
| bl | bl | 二進制數(shù)據(jù)解析 |
| bluebird | bluebird | 異步操作管理 |
| browserify | browserify | 發(fā)布瀏覽器可用的包 |
| bunyan | bunyan | 日志(logging)管理 |
| chai | chai | 斷言 |
| chalk | chalk | 命令行彩色輸出 |
| co | co | 異步流程管理 |
| colors | colors | 命令行彩色輸出 |
| commander | commander | 命令行工具 |
| debug | debug | Debug輸出器 |
| dockerode | dockerode | Docker管理 |
| duplexify | duplexify | Stream流操作工具 |
| event-stream | event-stream | Stream流操作工具 |
| express | express | Server服務(wù)器框架 |
| hapi | hapi | Server服務(wù)器框架 |
| koa | koa | Server服務(wù)器框架 |
| glob | glob | 文件名匹配 |
| grunt | grunt | 構(gòu)建工具 |
| gulp | gulp | 構(gòu)建工具 |
| hyperquest | hyperquest | 輕量級HTTP客戶端 |
| istanbul | istanbul | 測試用例覆蓋率分析 |
| JSONStream | JSONStream | Stream流管理工具 |
| levelup | levelup | LevelDB |
| lodash | lodash | 函數(shù)式編程工具 |
| log4js | log4js | 日志(logging)管理工具 |
| minimatch | minimatch | 文件名匹配 |
| minimist | minimist | 命令行操作 |
| mocha | mocha | 單元測試 |
| moment | moment | 日期時間輸出 |
| mongodb | mongodb | MongoDB |
| mysql | mysql | MySQL |
| nconf | nconf | 配置工具 |
| needle | needle | 輕量級HTTP客戶端 |
| node-fetch | node-fetch | Fetch API |
| nodemailer | nodemailer | Email客戶端 |
| passport | passport | 登錄和認證 |
| pg | pg | Postgres |
| pump | pump | Stream流管理工具 |
| redis | redis | Redis |
| request | request | HTTP客戶端 |
| restify | restify | REST API搭建 |
| socket.io | socket.io | WebSocket實時通信 |
| split2 | split2 | Stream流管理工具 |
| tape | tape | 單元測試 |
| through2 | through2 | Stream流管理工具 |
| underscore | underscore | 函數(shù)式編程工具 |
| ws | ws | Websockets |
| xml2js | xml2js | XML轉(zhuǎn)換為JavaScript |
| http-server | http-server | 命令行的HTTP服務(wù)器 |
| nrm | nrm | 更改NPM下載源 |
| node-inspector | node-inspector | Node調(diào)試 |
| supervisor | supervisor | 檢測Node進程的服務(wù) |
| nodemon | nodemon | 在文件有變化之后會自動重啟服務(wù) |
希望本文所述對大家node.js程序設(shè)計有所幫助。
相關(guān)文章
node.js express中app.param的用法詳解
express.js是nodejs的一個MVC開發(fā)框架,并且支持jade等多種模板。下面這篇文章主要給大家介紹了關(guān)于node.js express中app.param用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。2017-07-07
使用cluster 將自己的Node服務(wù)器擴展為多線程服務(wù)器
nodejs在v0.6.x之后 增加了一個模塊 cluster 用于實現(xiàn)多進程,利用child_process模塊來創(chuàng)建和管理進程,增加程序在多核CPU機器上的性能表現(xiàn)。本文將介紹利用cluster模塊創(chuàng)建的多線程的問題。2014-11-11
教你在heroku云平臺上部署Node.js應(yīng)用
heroku是構(gòu)建在AWS之上的一個PaaS云平臺,現(xiàn)在支持Ruby, Node.js, Python, Java, 和 PHP,代碼的部署是通過git進行,編譯和運行都是自動的。2014-07-07
node.js中的fs.createWriteStream方法使用說明
這篇文章主要介紹了node.js中的fs.createWriteStream方法使用說明,本文介紹了fs.createWriteStream方法說明、語法、接收參數(shù)、使用實例和實現(xiàn)源碼,需要的朋友可以參考下2014-12-12
NodeJS實現(xiàn)視頻轉(zhuǎn)碼的示例代碼
本篇文章主要介紹了NodeJS實現(xiàn)視頻轉(zhuǎn)碼的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
基于Node.js模板引擎教程-jade速學與實戰(zhàn)1
下面小編就為大家?guī)硪黄贜ode.js模板引擎教程-jade速學與實戰(zhàn)1。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09

