nodejs 入門

Node.js 不是一門語言也不是庫或框架,而是一個 js運行時環境,簡單來講就是 Node 能夠解析和執行js代碼
之前只有瀏覽器的 js 引擎可解析執行 js 代碼,後來谷歌瀏覽器的V8 js引擎被移植出來開發了 Node 這個獨立的 js 運行時環境,如今依靠它,js 就可徹底脫離瀏覽器來運行
但 Node 中的 js 有些不一樣,沒有BOM、DOM,加了不少的服務器級別的API,如文件讀寫、網絡服務構建、網絡通訊等
 
官網 的下載版本有兩個 ,LTS(Long Term Support)長期支持版,也就是穩定版,Current爲體驗版,有着最新特性
下載安裝後命令行輸入
node --version  node -v
便可確認是否安裝成功
 
而後隨便建立一個 js 文件,如在F盤新建名爲 nodeDemo 的文件夾,在裏面新建 helloworld.js,文件名不要使用 node.js , 最好也不要使用中文
var str = "Hello World~"; console.log(str);
 
再在這個 js 文件所在目錄打開 DOS 窗口,用 node 文件名 的命令來解析執行對應的文件,此時 helloworld.js 文件就脫離了瀏覽器,由 Node 執行


文件讀寫html

瀏覽器的 js 是不能對磁盤文件進行操做的,但 Node 中的 js 具備文件操做的能力node

在 nodeDemo 下新建 readFile.js 和 book.txt,book純文本里內容只有 「 ABC 」npm

  readFile.js
var fs = require('fs'); //使用require方法加載fs核心模塊
fs.readFile('./book.txt',function (error,data) { //點表示當前目錄,點點是上級目錄
    if(error) console.log('文件讀取失敗'); else console.log(data); }); /*readFile()參數有兩個,文件路徑和回調函數 回調函數有兩個形參,error和data 讀取成功: data爲數據, error爲null 讀取失敗: data爲undefined, error爲錯誤對象*/

fs 是 file system 的簡寫,文件系統的意思,在 Node 中要進行文件操做就必須引入 fs 這個核心模塊,在這個核心模塊中,提供了全部文件操做相關的 API,如 fs.readFile 就是用來讀取文件的。全部文件操做的API都是異步的編程

ABC對應的ASCII十進制爲 65 66 67,文件都是以二進制進行存儲,這裏輸出了對應的十六進制。調用 toString 方法便可輸出 ABCjson

console.log(data.toString());
 

 文件寫入也一樣簡單瀏覽器

//writeFile()有三個形參:文件路徑,文件內容,回調函數 //寫入成功時error爲null,失敗時爲錯誤對象
var fs = require('fs'); fs.writeFile('./song.txt','街燈晚餐',function (error) { if(error) console.log('文件寫入失敗'); else console.log('文件寫入成功'); });
若是寫入的文件本來就有內容則會將其覆蓋 文件若是事先不存在,則會自動建立,但若是這個文件所在的文件夾不存在,則會寫入失敗
簡單的http服務
使用 Node 能夠很輕鬆地構建一個 Web 服務器,nodeDemo 目錄下建立 http.js
http.js
//使用Node能夠很輕鬆地構建一個Web服務器,在Node中專門提供了一個核心模塊http
var http = require('http'); //加載http核心模塊
var server = http.createServer(); //建立一個Web服務器

/*註冊request請求事件,當客戶端請求時就會觸發此事件執行回調函數 回調函數有兩個形參 request和response,表示請求對象和響應對象 */ server.on('request',function (request,response) { console.log('接收到了來自' + request.url + '的請求'); response.write('Hi~'); response.end(); //response對象的write()方法可屢次使用但最後必定要用end()來結束響應,不然瀏覽器會一直等待
    //也可用簡單的方式:response.end('Hi~'); 響應的內容只能是二進制Buffer或字符串string
}); //綁定端口號,啓動服務器
server.listen(5000,function () { console.log('服務器啓動成功'); });
端口號範圍從 0 到 65535,qq爲4000,tomcat服務器爲8080......選個不被佔用的端口號便可  
執行後服務器成功開啓,此時程序就佔用了 DOS 窗口,等待客戶端的請求。這時關閉窗口或 Ctrl+C 則會中止服務器

經過 ip 可知道是哪臺電腦,經過端口號知道是電腦裏的哪一個程序,而 ip 127.0.0.1 表明本機,因此在瀏覽器地址欄輸入 http://127.0.0.1:5000 可訪問剛纔建立的服務器緩存

端口號80是網頁服務器默認的訪問端口,若是不寫端口號 http://127.0.0.1 ,瀏覽器會默認加上 http://127.0.0.0:80tomcat

 

服務器收到請求,並作出了響應。request.url 獲取端口號後面的那一部分路徑,表明根目錄。如請求 http://baidu.com服務器

就算末尾不加 / ,瀏覽器也會默認幫加上網絡

/favicon.icon 是瀏覽器默認會請求的 ico 圖標

在這個程序裏,不管是請求 /a 仍是 /a/b 都會觸發 request 事件,從而響應一樣的內容
 
中文亂碼

服務器響應的內容若是有中文,瀏覽器顯示時會出現亂碼,這是由於服務器以 utf-8 編碼響應數據,而中文瀏覽器默認以 gbk 編碼去解析

解決辦法是設置響應頭信息

var http = require('http'); var server = http.createServer(); server.on('request',function (request,response) { response.setHeader('Content-Type','text/plain;charset=utf-8'); //要放在write()以前
    response.write('Hi~你好啊'); response.end(); }); server.listen('3000',function () { console.log('Web Server running'); });

不一樣的資源對應不一樣的 Content-Type ,普通文本是 text/plain,html 是 text/html,更多類型可查看網上的 對照表

通常只有字符數據才指定編碼,圖片不須要指定編碼 

response.setHeader('Content-Type','image/jpeg'); 

核心模塊

Node 爲 js 提供了不少服務器級別的API,這些API絕大多數都被包裝一個具名的核心模塊中

如文件操做的 fs核心模塊、http 服務構建的 http模塊path路徑操做模塊os操做系統信息模塊

用到哪一個模塊都要必需要用 require() 方法來加載

Node支持模塊化編程,模塊分爲 具名核心模塊(Node內置的模塊) 、第三方模塊 和 自定義模塊(用戶本身寫的js文件)

 
Node 中沒有全局做用域只有模塊做用域,模塊做用域簡單地說就是文件做用域,超出這個文件的就沒法訪問

require() 加載只能是執行模塊中的代碼,模塊是徹底封閉的,加載執行多個文件時即使變量重名也不會有污染問題

若是在瀏覽器,b.js 的 str 會覆蓋前面的 str,因此輸出結果應該是 substring,但在這裏因爲模塊做用域,就算 a.js 加載了 b.js,也沒法使用 b.js 裏的變量或函數

 
那如何讓模塊之間進行通訊?每一個文件模塊中都提供了一個 exports 對象,而 require() 方法會返回這個對象,exports 默認是個空對象,即{},能夠把外部要訪問的成員掛載在此對象中,即經過點語法,爲 exports 對象添加屬性或方法

 
require() 有兩個做用:執行被加載模塊中的代碼、獲得被加載模塊中exports導出接口對象
exports 導出多個成員
exports.n = 123 exports.s = 'Hi' exports.f = function(){ console.log('fun') } exports.o = { str:'Hello' }

導出單個成員,若是一個模塊須要直接導出某個成員而非掛載的方式,這時候必須使用 module.exports

module.exports = 'Hi' module.exports. = { str:'Hello' }  //同時寫的話此字面量對象會覆蓋上面的 'Hi' 字符串 //module.exports若是想導出多個成員,可把放成員放一個對象中
 
模塊原理
Node 中每一個模塊內都有一個本身的 module 對象
module 對象中有一個對象成員叫 exports 
而且底層代碼最後會返回 module.exports,因此誰 require 我,誰就獲得 module.exports
新建一個 test.js 文件,只寫一行代碼,直接輸出 module對象
test.js
console.log(module);

 
Node 底層幫建立了 module對象,因此在 test.js 能夠看做有以下代碼存在
var module = {  exports:{} }  console.log(module) //本身寫的代碼

return module.exports
 
因此若是須要對外導出成員的話,須要把成員掛載到 module 的 exports 對象中,外部文件經過 require 拿到 exports 對象,即可獲取該對象的屬性
var module = {   
 exports:{} } console.log(module) //本身寫的代碼 module.exports.name = 'Sam' //經過點語法爲exports對象建立name屬性,屬性值爲字符串Sam

return module.exports
 
但每次都經過 module.exports.xxx 方式有點繁瑣,因此 Node 爲了簡化操做,提供了一個 exports變量,該變量指向 module.exports
因此 exports === module.exports 爲 ture
var module = { exports:{} } var exports = module.exports console.log(module) 
//module.exports.name = 'Sam' 
export.name = 'Sam' //二者指向相同,操做的是同一對象
return module.exports
可看到 export 和 module.exports ,用哪一個都同樣,惟一的區別是當導出的是單個成員時只能使用 module.exports,例如想直接返回字符串Sam,而不想把  Sam 放在對象屬性中
此時只能用 module.exports = 'Sam',而不能用 exports = 'Sam' ,由於 module.exports 和 exports 這兩個引用存儲的是 module 裏 exports實例對象 的地址值,賦值後,二者的地址值都會被覆蓋,但最後 return 的是 module.exports,而不是 exports    
 
加載規則 
模塊的加載會優先從緩存中加載

main 中加載 a , a 裏又加載b,當代碼回來執行 main 中的 require 時,因爲 a 已經加載過 b 了,因此 main 不會再重複加載,能夠拿到返回的接口對象但不會再執行代碼
這樣作的目的是避免重複加載,底層會優先從緩存中加載,加載模塊時會看緩存裏有沒有,有就用,沒有就加載
 
require方法模塊查找機制
require( ' 模塊標識 ' )
若是模塊標識爲路徑形式,會從給定的路徑加載,若是是非路徑的標識,就只有兩種狀況,核心模塊或第三方

 
拿第三方模塊 art-template 舉栗子,在當前目錄用 npm下載好 art-template 後,會有個 node_modules 文件夾
require('art-template')
不是路徑模塊標識,也不是核心模塊,因此 Node 會當第三方模塊進行加載,先在當前文件所在的目錄找到 node_modules 文件夾,而後找            node_modules/art-template    
node_modules/art-template/package.json

 

再找 package.json 裏的 main 屬性,main 記錄了 art-template 的入口模塊

 

最終加載的是 index.js 裏的內容, index.js 裏又要加載其餘模塊,而後最後導出 template 這個對象。實際上最終加載的仍是文件,而核心模塊的本質也是文件

若是 package.json 不存在或 main 指定的入口不存在,則 Node 會自動找到該目錄下的 index.js,也就是 index.js 會做爲一個默認備選項 若是以上條件都不成立,則會進入上一級目錄的 node_modules 裏查找
上一級尚未,就繼續上上一級查找
直到當前磁盤根目錄還找不到,就會報錯:can not find module xxx  
注意:一個項目有且只有一個 node_modules,放在項目根目錄中,這樣的話全部子項目都能加載  
 
更多底層細節,可參考《深刻淺出Node.js》中的模塊系統章節
相關文章
相關標籤/搜索