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應用程式的硬體成本就上升了。
單執行緒好處:作業系統完全不再有執行緒建立,銷燬的時間開銷。
單執行緒壞處:就是一個使用者造成了執行緒的奔潰,整個服務都奔潰了,其它人的服務也就奔潰了。
單執行緒也能夠造成巨集觀上的“併發”。
非阻塞I/O
非阻塞I/O non-blocking I/O
例子:訪問資料庫取得資料的時候,需要一段時間。
在傳統的單執行緒處理機制中,在執行了訪問資料庫程式碼之後,整個執行緒都將暫停下來,等待資料庫返回結果,才能執行後面的程式碼。也就是說I/O阻塞了程式碼的執行,極大降低了程式執行的效率。
Node採用了非阻塞型I/O機制,因此在執行了訪問資料庫的程式碼之後,將立即轉而執行後面的程式碼,把資料庫返回的結果的處理程式碼放在回撥函式中,從而提高了程式的執行效率。
當某個I/O執行完畢時,將以時間的形式通知執行I/O操作的執行緒,執行緒執行了這個事件的回撥函式。為了處理非同步I/O,執行緒必須有事件迴圈,不斷的檢查是否有未處理的時間。依次予以處理。
阻塞模式下,一個執行緒只能處理一項任務,要想提高吞吐量必須通過多執行緒。而非阻塞模式下,一個執行緒永遠在執行計算操作,這個執行緒的CPU核心利用率永遠是100%。 有一種類似 : 與其多人工作,但是好多人閒著,倒不如一個人工作,往死裡幹活。
事件驅動
事件驅動 event-driven
在Node中,客戶端請求建立連線,提交資料等行為,會觸發相應的時間。在Node中,在一個ie時時刻,只能執行一個事件回撥函式,但是在執行一個事件回撥函式的中途,可以轉而處理其它事件(比如:有新使用者連線),然後返回繼續執行原事件的回撥函式。這種處理機制,稱為:"事件環"機制。
Node底層是C++(V8也是C++) 編寫。底層程式碼中,近半數都使用者事件佇列,回撥函式佇列的構建。用事件驅動來完成伺服器的任務排程。用一個執行緒,擔負起了處理非常多的任務。
單執行緒,減少記憶體開銷,作業系統的記憶體換頁。
如某一個任務,執行了,但是被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上行請求頭
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");
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包:
- express
- request (後端傳送請求的模組)
- cheerio (像前端一樣操作拉取回來的資料)
爬蟲以及Robots協議介紹:
- 爬蟲,是中自動獲取網頁內容的程式。是搜尋引擎的重要組成部分,因此搜尋引擎優化很大程度上就是針對爬蟲而做出的優化。
- robots.txt是一個文字檔案,robots.txt是一個協議,不是一個命令。robots.txt是爬蟲要檢視的第一個檔案。robots.txt檔案告訴爬蟲在伺服器上什麼檔案是可以被檢視的,搜尋機器人就會按照該檔案中的內容來確定訪問的範圍。
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程式設計有所幫助。