3.1Nodejs編程
3.1.1 Hello world
打開記事本
console.log('Hello world!');
3.1.2 node.js命令行工具
3.1.3 建立http服務器
node.js最簡單的http服務器就這樣誕生了,這個程序調用了nodejs提供的http模塊,對所用HTTP請求答復同樣的內容并監聽3000端口。
小技巧--使用supervisor
改動nodejs代碼自動重啟服務器。
npm install -g supervisor
3.2 異步式I/O和事件式編程
nodejs最大的特點就是異步式I/O(或者非阻塞I/O)與事件緊密結合的編程模式,這種編程模式與傳統的同步式I/O線性編程思路有很大的不同,因為控制流很大程度上要靠事件和回調函數來組織,一個邏輯要拆分為若干個單元。
3.2.1 阻塞與線程
什么是阻塞(block)呢?線程在執行中如果遇到磁盤讀寫或網絡通信(統稱I/O操作),通常要耗費較長的時間,這時操作系統會剝奪這個線程的CPU控制權,使其暫停執行,同時將資源讓給其他的工作線程,這種線程調度方式稱為阻塞。當I/O操作完畢時。操作系統將這個線程的阻塞狀態刪除,恢復其對CPU的控制權。令其繼續執行。這種I/O模式就是通常的同步式I/O(Synchronous I/O)或阻塞式I/O(Blocking I/O)。
異步式I/O(Asynchronous I/O)或非阻塞式I/O(Non-blocking I/O)則針對所有的I/O操作不采用阻塞的策略。當線程遇到I/O操作時,不會以阻塞的方式等待I/O操作的完成或數據的返回,而只是將I/O請求發送給操作系統,繼續執行下一條語句。當操作系統完成I/O操作時,以事件的形式通知執行I/O操作的線程,線程會在特定時間處理這個事件,為了處理異步I/O,線程必須有事件循環,不斷地檢查有沒有處理的事件,依次予以處理。
阻塞模式下,一個線程處理一項任務,多線程提高系統吞吐量。
非阻塞模式下,一個線程永遠在執行計算操作,CPU利用率100%,I/O以事件方式通知。線程不會被I/O阻塞,永遠在利用CPU。
優點:單線程事件驅動的異步式I/O比傳統的多線程阻塞式I/O好在哪里?
異步式I/O少了多線程的開銷,對操作系統來說,創建一個線程的代繳是十分昂貴的,需要給他分配內存,列入調度,同時在線程切換的時候執行內存換頁,CPU的緩存被清空,切換回來的時候還要重新從內存中讀取信息,破壞了數據的局部性。
缺點:不符合人們一般的程序設計思維,容易讓控制流變得晦澀難懂,給編碼和調試帶來不小的困難。習慣傳統編程模式的開發者在剛剛接觸到大規模的異步應用時往往會無所適從,有不少解決異步式編程問題的庫。
3.2.2 回調函數
nodejs中如何用異步的方式讀取一個文件
代碼如下:
結果如下:
同步式讀取文件
代碼如下:
結果如下:
同步式讀取文件的方式比較好理解。
異步式讀取文件:異步式I/O請求發送給了操作系統,然后立即返回并執行后面的語句,執行完以后進入事件循環監聽事件,當fs接收到I/O請求完成的事件時,事件循環會主動調用回調函數以完成后續工作。
3.2.3 事件
3.3 模塊和包
一個node.js文件就是一個模塊(這個文件可能是javascript代碼、json或者編譯過的c/c++擴展)
3.3.2
1、創建模塊
module.js
//module.js
var name;
exports.setName = function(thyName){
name = thyName;
};
exports.sayHello = function(){
console.log('hello'+ name);
};
在同一目錄下創建getmodule.js
//getmodule.js
var myModule = require('./module');
myModule.setName('BYVoid');
module.js通過exports對象把setName和sayHello作為模塊的訪問接口,在getmodule.js中通過require('./module')加載這個模塊,然后就可以直接訪問module.js中exports對象的成員函數了。
2.單次加載
無論調用多少次require。獲得的模塊都是同一個
- 覆蓋exports
3.3.3創建包
node.js的包是個目錄。
CommonJS規范的包應該具備以下特征:
package.json 必須在包的頂層目錄下
二進制文件應該在bin目錄下
javascript代碼必須在lib目錄下
文檔應該在doc目錄下
單元測試應該在test目錄下
1 作為文件i一個叫做somepackage的文件夾,在其中創建index.js
//somepackage/index.js
exports.hello = function(){
console.log('hello.');
};
然后在somepackage之外建立getpackage.js
//getpackage.js
var somePackage = require('./somepackage');
somePackage.hello();
運行 node getpackage.js 控制臺將輸出結果hello .
我們把文件夾封裝成模塊,即所謂的包,包通常是一些模塊的集合,在模塊的基礎上提供了更高的抽象,相當于提供了一些固定接口的函數庫,通過定制package.json,我們可以創建更復雜、更完善、更符合規范的包用于發布。
2 package.json
在somepackage中創建package.json
{
'main':'./lib/interface.js'
}
然后將index.js重命名為interface.js并放入lib子文件下。以同樣的方式再次調用這個包,依然可以正常使用。
node.js在調用某個包時,會首先檢查包中package.json文件的main字段,將其作為包的接口模塊,如果package.json或main字段不存在,會嘗試尋找index.js或index.node作為包的接口。
package.json 是CommonJS規定的用來描述包的文件,完全符合規范的package.json文件應該含有以下字段
name:包的名稱,必須唯一,由小寫英文字母、數字和下劃線組成,不能包含空格
description:包的簡要說明
version:符合語義化版本識別規范的版本字符串
keywords:關鍵詞數組,通常用于搜索
maintainers:維護者數組,每個元素要包含name、email(可選)、web(可選)字段
contributors:貢獻者數組,格式與maintainers相同,包的作者應該是貢獻者數組的第一個元素
bugs:提交bug的地址,可以是網址或者電子郵件地址
licenses:許可證數組,每個元素要包含type(許可證的名稱)和Url(鏈接到許可證文本的地址)字段
repositories:倉庫托管地址數組,每個元素要包含type(倉庫的類型,如git)、url(倉庫地址)和path(相對于倉庫的路徑,可選)字段
dependencies:包的依賴,一個關聯數組,由包名稱和版本號組成
{
"name": "mypackage",
"description": "Sample package for CommonJS. This package demonstrates the requiredelements of a CommonJS package.",
"version": "0.7.0",
"keywords": ["package","example"],
"maintainers": [{"name": "Bill Smith","email": "bills@example.com",}],
"contributors": [{"name": "BYVoid","web": "http://www.byvoid.com/"}],
"bugs": {"mail": "dev@example.com","web": "http://www.example.com/bugs"},
"licenses": [{"type": "GPLv2","url": "http://www.example.org/licenses/gpl.html"}],
"repositories": [{"type": "git","url": "http://github.com/BYVoid/mypackage.git"}],
"dependencies": {"webkit": "1.2","ssl": {"gnutls": ["1.0", "2.0"],"openssl": "0.9.8"}}
}
3.3.4 Nodejs包管理
1獲取一個包
npm [install/i] [package_name]
例如你要安裝express
npm install express
2npm全局模式
npm install -g supervisor:同時在PATH環境變量中注冊supervisor
在mac/Linux 全局模式安裝需要root權限
3創建全局連接
全局模式安裝的包不能通過require使用,但是通過npm link 可以打破這一局限(不支持windows)
npm link express
./node_modules/express > /usr/local/bin/node_modules/express
除了將全局的包連接到本地以外,npm link還可以將本地的包連接到全局,使用方法(在package.json所在目錄)中運行npm link,如果我們要開發一個包,利用這種方法可以非常方便地在不同的工程間進行測試。
4包的發布
npm 可以非常方便地發布一個包,比 pip、 gem、 pear 要簡單得多。在發布之前,首先需要讓我們的包符合 npm 的規范, npm 有一套以 CommonJS 為基礎包規范,但與 CommonJS并不完全一致,其主要差別在于必填字段的不同。通過使用 npm init 可以根據交互式問答產生一個符合標準的 package.json,例如創建一個名為 byvoidmodule 的目錄,然后在這個目錄中運行npm init:
$ npm init
Package name: (byvoidmodule) byvoidmodule
Description: A module for learning perpose.Package
version: (0.0.0) 0.0.1
Project homepage: (none) http://www.byvoid.com/
Project git repository: (none)
Author name: BYVoidAuthor
email: (none) byvoid.kcp@gmail.com
Author url: (none) http://www.byvoid.com/
Main module/entry point: (none)Test command: (none)
What versions of node does it run on? (~0.6.10)
About to write to/home/byvoid/byvoidmodule/package.json
{
"author": "BYVoid <byvoid.kcp@gmail.com>(http://www.byvoid.com/)",
"name": "byvoidmodule",
"description": "A module for learning perpose.",
"version": "0.0.1",
"homepage": "http://www.byvoid.com/",
"repository": {
"url": ""
},
"engines":{
"node": "~0.6.12"
},
"dependencies": {},
" devDependencies": {}
}
Is this ok? (yes) yes
這樣就在 byvoidmodule 目錄中生成一個符合 npm 規范的 package.json 文件。創建一個index.js 作為包的接口,一個簡單的包就制作完成了。在發布前,我們還需要獲得一個賬號用于今后維護自己的包,使用 npm adduser 根據提示輸入用戶名、密碼、郵箱,等待賬號創建完成。完成后可以使用 npm whoami 測驗是否已經取得了賬號。接下來,在 package.json 所在目錄下運行 npm publish,稍等片刻就可以完成發布了。打開瀏覽器,訪問 http://search.npmjs.org/ 就可以找到自己剛剛發布的包了。現在我們可以在世界的任意一臺計算機上使用 npm install byvoidmodule 命令來安裝它。圖3-6 是npmjs.org上包的描述頁面。
如果你的包將來有更新,只需要在 package.json 文件中修改 version 字段,然后重新使用 npm publish 命令就行了。如果你對已發布的包不滿意(比如我們發布的這個毫無意義的包),可以使用 npm unpublish 命令來取消發布。
3.4 調試
寫一個簡單的代碼
在命令行下執行 node debug debug.js,將會啟動調試工具:
3.4.2 遠程調試
3.4.4 使用node-inspector調試nodejs
大部分基于 Node.js 的應用都是運行在瀏覽器中的,例如強大的調試工具 node-inspector。node-inspector 是一個完全基于 Node.js 的開源在線調試工具,提供了強大的調試功能和友好的用戶界面,它的使用方法十分簡便。首先,使用 npm install -g node-inspector 命令安裝 node-inspector,然后在終端中通過 node --debug-brk=5858 debug.js 命令連接你要除錯的腳本的調試服務器,啟動 node-inspector:$ node-inspector在瀏覽器中打開 http://127.0.0.1:8080/debug?port=5858, 即可顯示出優雅的 Web 調試工具