1. 程式人生 > 程式設計 >Node 模組原理與用法詳解

Node 模組原理與用法詳解

本文例項講述了Node 模組原理與用法。分享給大家供大家參考,具體如下:

簡介

V8引擎本身就是用於Chrome瀏覽器的JS解釋部分,但是Ryan Dahl,把V8搬到伺服器,用於做伺服器的軟體。
Node是一個專注於實現高效能Web伺服器優化的專家,在遇到V8而誕生的專案

  • 沒有歷史包袱,沒有同步I/O。不會出現一個同步I/O導致事件迴圈效能急劇降低的情況。
  • V8效能足夠好,遠遠比Python,Ruby等其它指令碼語言的引擎快。
  • JavaScript語言的閉包特性非常方便,比C中的回撥函式好用。

Node可以讓JavaScript執行在伺服器端的平臺開發,它讓JavaScript的觸角延伸到了伺服器端,可以與PHP,JSP,Python,Ruby等語言實現後端開發。

但Node似乎有點不同:

  • Node不是一種獨立的語言,與PHP,JSP,Python,Perl,Ruby的“即使語言,也是平臺”不同,Node使用的是JavaScript進行程式設計,執行在JavaScript引擎上(V8)
  • 與PHP,JSP等相比(PHP,JSP,.net都需要執行在伺服器程式上,Apache,Naginx,Tomcat,IIS),Node跳過了Apcahe,Naginx,IIS等HTTP伺服器,它自己不用建設在任何伺服器任何之上。Node的設計理念與經典架構(LAMP = Linux + Apache + MySQL + PHP) 有著很大的不同,可以提供強大的伸縮能力。
  • Node沒有Web容器。
  • Node是花最小的硬體成本,追求更高的併發,更高的處理效能。

Node特點

所謂特點,就是Node如果解決伺服器高效能瓶頸問題。
JavaScript有什麼特點的時候,會立即想到 單執行緒事件驅動面向物件。但是JavaScript精髓 覺得是 this閉包作用域鏈函式。才使得這門語言魅力無窮。

單執行緒

在Java,PHP,或者.net 等伺服器端語言中,會為每一個使用者端連線建立一個新的執行緒。而每個執行緒需要耗費大約2MB記憶體。理論上,一個8GB記憶體的伺服器可以同時連線的最大使用者數4000個左右。要讓Web應用程式支援更多的使用者,就需要增加伺服器的數量,而Web應用程式的硬體成本就上升了。

Node不為每個使用者連線建立一個新的執行緒,而僅僅使用一個執行緒。當有使用者連線了,就觸發一個內部事件,並通過非阻塞I/O,事件驅動機制,讓Node程式巨集觀上也是並行的。Node中,一個8GB記憶體的伺服器,可以同時處理超過4萬用戶的連線。
單執行緒好處:作業系統完全不再有執行緒建立,銷燬的時間開銷。
單執行緒壞處:就是一個使用者造成了執行緒的奔潰,整個服務都奔潰了,其它人的服務也就奔潰了。

Node 模組原理與用法詳解

Node 模組原理與用法詳解

單執行緒也能夠造成巨集觀上的“併發”。

非阻塞I/O

非阻塞I/O non-blocking I/O

例子:訪問資料庫取得資料的時候,需要一段時間。
在傳統的單執行緒處理機制中,在執行了訪問資料庫程式碼之後,整個執行緒都將暫停下來,等待資料庫返回結果,才能執行後面的程式碼。也就是說I/O阻塞了程式碼的執行,極大降低了程式執行的效率

Node採用了非阻塞型I/O機制,因此在執行了訪問資料庫的程式碼之後,將立即轉而執行後面的程式碼,把資料庫返回的結果的處理程式碼放在回撥函式中,從而提高了程式的執行效率。
當某個I/O執行完畢時,將以時間的形式通知執行I/O操作的執行緒,執行緒執行了這個事件的回撥函式。為了處理非同步I/O,執行緒必須有事件迴圈,不斷的檢查是否有未處理的時間。依次予以處理。
阻塞模式下,一個執行緒只能處理一項任務,要想提高吞吐量必須通過多執行緒。而非阻塞模式下,一個執行緒永遠在執行計算操作,這個執行緒的CPU核心利用率永遠是100%。 有一種類似 : 與其多人工作,但是好多人閒著,倒不如一個人工作,往死裡幹活。

Node 模組原理與用法詳解

事件驅動
事件驅動 event-driven

在Node中,客戶端請求建立連線,提交資料等行為,會觸發相應的時間。在Node中,在一個ie時時刻,只能執行一個事件回撥函式,但是在執行一個事件回撥函式的中途,可以轉而處理其它事件(比如:有新使用者連線),然後返回繼續執行原事件的回撥函式。這種處理機制,稱為:"事件環"機制。

Node底層是C++(V8也是C++) 編寫。底層程式碼中,近半數都使用者事件佇列,回撥函式佇列的構建。用事件驅動來完成伺服器的任務排程。用一個執行緒,擔負起了處理非常多的任務。

Node 模組原理與用法詳解

單執行緒,減少記憶體開銷,作業系統的記憶體換頁。
如某一個任務,執行了,但是被I/O阻塞了,所以這個縣城就阻塞了。非阻塞I/O,不會傻等I/O語句結束,而會執行後面的語句。利用事件驅動,不管是新使用者的請求,還是老使用者的I/O完成,都將以事件方式加入事件環中,等待排程。

Node所有的I/O都是非同步的,回撥函式巢狀回撥函式。

Node是單程序單執行緒應用程式,但是通過事件和回撥支援併發,所以效能非常高。
Node的每個API都是非同步的,並作為一個獨立執行緒執行,使用非同步函式呼叫,並處理併發。
Node基本上所有的事件機制都是用設計模式中的觀察者模式實現的。
Node單執行緒類似進入一個while(true)的事件迴圈,直到沒有事件觀察者退出,每個非同步事件都生成一個事件觀察者,如果有事件發生就呼叫該回調函式。

模組

moduel

Node中,以模組為單位劃分所有功能,並且提供一個完整的模組載入機制,可以將應用程式話費為各個不同的部分。

Node中,一個JavaScript檔案中定義的變數,函式,都只在這個檔案內部有效果。

俠義的說,每一個JavaScript檔案都是一個模組,而多個JavaScript檔案之間可以相互require,共同實現一個功能,整體外對,又稱之為廣義上的模組

好處:

  • 減少重複程式碼量,增加可讀性。
  • 方便進行程式碼規劃。
  • 方面使用第三方模組。

當需要從JS檔案外部引用到這些變數,函式時,必須使用exprots物件,或者module.exprots進行暴露。使用者需要使用require(); 函式引入這個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無數個變數,函式,物件,但是require(); 的時候,僅僅需要 載入一次JS檔案即可。 所以,無形之後,會增加一個頂層名稱空間。

// 變數
// 需要變數引用 使用 
exports.a = 10;
// 直接需要變數值使用.
module.exports = name;

// 物件
module.exports = {
 name1: 123,name2: 456
}
// 暴露結果: { name1: 123,name2: 456 }

exports.msg = {
 name1: 1,name2: 2
}
// 暴露結果 : { msg: { name1: 1,name2: 2 } }



// 函式
exports.showMsg = function () {
}

// 暴露結果 : { showMsg: [Function] }
// 在 引用結果 需要 通過 變數 引用物件 執行
// var msg = require();
// msg.showMsg();


module.exports = function () {
}
// 暴露結果 [Function]
// 引入檔案的 變數 直接執行

模板引擎

資料繫結,成為一個完整的HTML字串。
Node中使用的模板:ejs 和 jade

後臺模板引擎:

<ul>
  <% for(var i = 0 ; i < news.length ; i++){ %>
   <li><%= news[i] %></li>
  <% } %>
</ul>

// 模板中需要的資料
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語法監聽某個事件。

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上行請求頭

Node 模組原理與用法詳解

req.httpVersion // http請求的版本。現在基本上都是1.1  
req.method  //  “GET”、”POST”就是請求的型別  
req.url // 使用者請求的網址

res物件

每次下行響應物件

res.end() // 每次都要有這個語句,表示這次的傳送已經結束
  // 引數必須是string、buffer(圖片、檔案)。

res.write()  // 就是寫HTTP下行請求的body

res.setHeader()     // 設定返回的報文頭部

res.statusCode = 404;  // 設定狀態碼

res.writeHead()     // 和res.setHeader差不多
res.writeHead(288,{"Content-Type":"text/plain"});

url模組

作用內建模組,解析url,解析地址。 分析和解析 URL 的工具

url.parse()

url.parse()就是用來解析網址,接收一個字串,返回一個JSON:

var obj = url.parse("http://localhost:8080/a/b/c/1.html?name=ting&age=21"); 

Node 模組原理與用法詳解

url.parse方法第二個引數如果是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模組

處理和轉換檔案路徑的工具集,專門處理路徑

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();      //讀取檔案內容  
fs.readDir();      //讀取資料夾名
fs.appendFile();    //追加檔案
fs.writeFile();     //寫入檔案(覆蓋原有的)
fs.rename();      //修改檔名

自定義模組

每一個js檔案中可以看成是一個小小的模組
require()誰,就會執行誰。就相當於呼叫一個函式。A require B, 先執行B全部語句,然後執行A的剩餘語句。

require('./test/a.js'); 

每個js檔案就是一個閉包,宣告的函式、變數只在這個js檔案內部有定義。
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 ,一般使用到建構函式。

如果只有寫檔案載入,會去預設資料夾下: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": [],//關鍵詞,就是搜尋什麼npm上能夠顯示你
 "author": "ag_dubs",//作者
 "license": "ISC",//版權協議
 "repository": {       //程式碼託管倉庫,這個會自動生成一個連線
  "type": "git","url": "https://github.com/ashleygwilliams/my_package.git"
 },"bugs": {        //如果發現bug應該交給誰
  "url": "https://github.com/ashleygwilliams/my_package/issues"
 },"dependencies": {
  "underscore": "*","date-format" : "0.0.2"
 },"homepage": "https://github.com/ashleygwilliams/my_package"  //個人網站
} 

最重要的資訊是:依賴

{
 "dependencies": { 
  "underscore": "*","date-format" : "^0.0.2"
 }
}

formidable

處理POST請求

// formidable 語法
var form = new formidable.IncomingForm();

// 設定上傳路徑
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包:

  1. express
  2. request (後端傳送請求的模組)
  3. cheerio (像前端一樣操作拉取回來的資料)

爬蟲以及Robots協議介紹:

  1. 爬蟲,是中自動獲取網頁內容的程式。是搜尋引擎的重要組成部分,因此搜尋引擎優化很大程度上就是針對爬蟲而做出的優化。
  2. robots.txt是一個文字檔案,robots.txt是一個協議,不是一個命令。robots.txt是爬蟲要檢視的第一個檔案。robots.txt檔案告訴爬蟲在伺服器上什麼檔案是可以被檢視的,搜尋機器人就會按照該檔案中的內容來確定訪問的範圍。

Node 模組原理與用法詳解

var express = require('express');
var app = express();
var cheerio = require('cheerio');

app.get('/',function(req,res){

  var request = require('request');

  request('https://linxingzhang.com',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 二進位制資料解析
bluebird bluebird 非同步操作管理
browserify browserify 釋出瀏覽器可用的包
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伺服器框架
hapi hapi Server伺服器框架
koa koa Server伺服器框架
glob glob 檔名匹配
grunt grunt 構建工具
gulp gulp 構建工具
hyperquest hyperquest 輕量級HTTP客戶端
istanbul istanbul 測試用例覆蓋率分析
JSONStream JSONStream Stream流管理工具
levelup levelup LevelDB
lodash lodash 函數語言程式設計工具
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 函數語言程式設計工具
ws ws Websockets
xml2js xml2js XML轉換為JavaScript
http-server http-server 命令列的HTTP伺服器
nrm nrm 更改NPM下載源
node-inspector node-inspector Node除錯
supervisor supervisor 檢測Node程序的服務
nodemon nodemon 在檔案有變化之後會自動重啟服務

希望本文所述對大家node.js程式設計有所幫助。