0. 小工具 npm: node第三方庫管理工具,用來獲取第三方包、升級、刪除或發佈編寫的包。 nvm: node的多版本管理工具。 supervisor:監視你對代碼的改動,重啓當前執行的js文件,通常用在網站、web開發以實時修改並觀察結果便於調試。 1. 事件 events內置模塊,可註冊事件監聽器、觸發指定的事件信號和相應的事件處理。 事實上,不少操做,如讀寫文件、http服務器、數據庫查詢等均內部使用events模塊的EventEmitter實現的。 2. 事件循環 Node執行過程當中有個事件循環,還有一個事件隊列,依次取出每一個事件以及相應的事件回調函數。 Node的事件循環對外不可見,內部底層由libuv庫實現支持。 3. 模塊和包 模塊以把功能拆分、封裝,而後組合起來,供其餘模塊使用,基本上認爲一個文件就是一個模塊(js文件、JSON、C/C++實現的擴展等,且Node會以.js、.json、.node的次序補足擴展名,依次嘗試分析加載)。 Node的模塊和包的實現機制,大多數是參照CommonJS的標準實現的。 模塊和包的區別,能夠認爲包是一個更大的模塊功能的集合體(一個目錄,在模塊基礎上的更高層次抽象,提供外部的一些相對固定的接口的函數庫),主要用於發佈、維護、更新等, 而對使用者來講,模塊和包的區別是透明的,所以常常不做區分。若是package.json或main字段不存在,會嘗試尋找index.js做爲包的接口。 屢次require同一個模塊,不會屢次重複加載該模塊,第一次加載時已緩存在內存,後面加載同一個模塊時直接從內存緩存讀取,不管內置模塊或文件模塊均會緩存。 4. 調試 調試方式:console.log等或者觀察-> FireBug、Chrome 開發者工具 -> V8支持的調試。 Node的命令行調試:node debug xxx.js,可開啓調試,進入debug調試後,可經過其餘的調試參數進行調試。 V8提供的遠程調試:基於TCP協議。 遠程:node --debug-brk xxx.js或 node --debug debug.js, 開啓調試服務器,可指定端口(=port)參數。 本地:node debug ip:port ip爲遠程IP地址,port爲遠程端口,默認爲5858,若指定了端口,則用遠程指定的端口鏈接。 PS: 命令行的調試採用的在本地執行了一個遠程和本地的兩條命令實現的,即node --debug xxx.js + node debug 127.0.0.1:5858 = node debug xxx.js。 此外還能夠經過某些IDE可方便的提供調試操做。 (新版本Node,建議使用node inspect替代) 5. 全局對象 node中全局對象:global對象,而瀏覽器通常爲windows對象;一樣的,除了全局對象外,其餘的全局變量可認爲均是全局對象的屬性。 通常狀況下:在最外層定義的變量(node中沒法定義最外層變量)、全局對象的屬性、隱式定義的變量(沒有使用var聲明,直接賦值的)均爲全局變量。 注意:儘可能不要使用全局變量,尤爲是沒有用var聲明定義的變量,形成命名空間污染,可能引入BUG。 process:一個全局變量,global對象的屬性。它用於描述當前Node.js進程狀態的對象,提供了一個與操做系統的簡單接口。 console: 控制檯輸出對象,console.log,console.error,console.warn,console.info,console.trace打印調用堆棧。 6. 經常使用的工具內置模塊util util.inherits(Sub, Base),提供從Base類中繼承原型鏈,而不是繼承Base類(屬性和方法)的工具函數(Sub僅繼承Base的原型鏈)(建議使用此方式實現繼承,避免多重繼承或深層次的繼承樹)。 util.inspect(object,[showHidden],[depth],[colors]),是一個將任意對象轉換爲字符串的方法,一般用於調試和錯誤輸出(不是經過調用對象的toString方法)。 此外還有不少如:util.isArray,util.isDate,util.isRegExp,util.isError等類型測試工具,util.format格式化等工具。 7. 最重要的內置模塊之一events事件模塊 該模塊不只用於用用戶代碼與Node下層事件循環交互,並且也是其餘幾乎全部模塊的依賴實現的基礎,以支持事件響應的核心模塊均是events.EventEmitter的子類。 events.EventEmitter對象:事件發射器,封裝了事件發射、若干事件監聽功能的封裝。 EventEmitter.on(event, listener)爲指定事件註冊一個監聽器,接受一個字符串event和一個回調函數listener。 EventEmitter.emit(event, [arg1], [arg2], [...])發射event事件,傳遞若干可選參數到事件監聽器的參數列表。 EventEmitter.once(event, listener)爲指定事件註冊一個單次監聽器,即監聽器最多隻會觸發一次,觸發後馬上解除該監聽器。 EventEmitter.removeListener(event, listener)移除指定事件的某個監聽器,listener必須是該事件已經註冊過的監聽器。 EventEmitter.removeAllListeners([event])移除全部事件的全部監聽器,若是指定event,則移除指定事件的全部監聽器。 EventEmitter對象提供一特殊事件error,遇到異常時發射error事件,當error被髮射時EventEmitter 規定若是沒有響應的監聽器, Node.js會把它看成異常,退出程序並打印調用棧。可爲error事件設定監聽器,以免被拋出異常未被捕獲而致使進程退出。 8. 文件系統內置模塊fs fs模塊提供文件操做的封裝,文件讀取、寫入、改名、刪除、目錄遍歷、連接等posix文件系統操做,且提供了同步和異步兩個版本的操做接口。 如:fs.readFile(filename,[encoding],[callback(err,data)]),若指定了encoding編碼,則data值爲轉化後的字符串,不然爲Buffer對象實例的二進制數據。 當讀取文件出現錯誤時,err將會是Error對象實例。 同步版本:fs.readFileSync(filename, [encoding]),讀取到的文件內容會以函數返回值的形式返回。若是有錯誤發生,fs將會拋出異常, 應使用try和catch捕捉並處理異常(與同步I/O函數不一樣,node的不少異步操做接口是沒有返回值的)。 其餘的如:fs.open,fs.read,fs.close,fs.readFile,fs.writeFile,fs.fsync等。 9. HTTP服務器與客戶端 http內置模塊,比較底層,其封裝了一個高效的HTTP服務器和一個簡易的HTTP客戶端。事實上http的目標不是僅僅做web服務器,還能夠作其餘的, 如可做爲網站、社交應用、代理服務器等,如手動實現HTPP服務器則須要實現更多的操做,故而通常使用封裝好了的框架,以提供高級接口和提升開發效率, 如egg,mean、Express、koa等。 http.Server是一個基於事件的HTTP服務器,它的核心由Node.js下層C++部分實現,而接口由 JavaScript 封裝,兼顧了高性能與簡易性。 http.request是一個HTTP客戶端工具,用於向HTTP服務器發起請求。 一樣的http.Server繼承於EventEmitter的,基於事件,其中有幾個比較重要的事件。
request事件:當客戶端請求到來時,該事件被觸發,提供兩個參數req和res,分別是http.ServerRequest和http.ServerResponse的實例,表示請求和響應信息。 connection事件:當TCP鏈接創建時,該事件被觸發,提供一個參數socket,爲net.Socket的實例。connection事件的粒度要大於request事件, 由於客戶端在Keep-Alive模式下可能會在同一個鏈接內發送屢次請求。 close事件:當服務器關閉時,該事件被觸發。注意不是在用戶鏈接斷開時。 此外還有checkContinue、upgrade、clientError事件。其中request事件可在CreateServer時指定,也能夠在建立server後,調用on("request", listener)也可。 http.ServerRequest是HTTP請求的信息,是後端開發者最關注的內容;通常在request事件的回調函數中。 http.ServerRequest提供了幾個事件用於控制請求體傳輸。如data、end、close事件,此外還有其餘的屬性可獲取到更多的信息。 獲取GET請求內容(URL中?後面的內容):Node.js的url模塊中的parse函數提供解析url的功能,可從解析內容對象中的query屬性來獲取到Get請求數據。 獲取POST請求內容:GET請求把全部的內容編碼到訪問路徑中(URL),POST請求的內容所有都在請求體中,Node.js默認是不會解析請求體的, 當須要的時候,需手動來獲取請求內容,如經過querystring.parse模塊解析獲取的數據內容以獲取到真正的POST請求格式。 http.ServerResponse是返回給客戶端的信息,決定了用戶最終能看到的結果。它也是由http.Server的request事件回調做爲第二個參數傳遞的。 此外其有三個函數: response.writeHead:向請求的客戶端發送響應頭,一次請求最多一次,若沒有調用則會默認生成響應頭給客戶端。 response.write:向請求的客戶端發送響應內容,若內容爲字符串,則需指定編碼方式,若爲Buffer對象則不須要;在調用response.end前可屢次調用。 response.end:結束響應,告知客戶端全部發送已經完成。當全部要返回的內容發送完畢的時候,該函數必須被調用一次。 它接受兩個可選參數,意義和response.write相同。若是不調用該函數,客戶端將永遠處於等待狀態。 http模塊提供了兩個函數http.request和http.get,功能是做爲客戶端向HTTP服務器發起請求。 http.request(options, callback):options爲關聯數組,可指定屢次參數內容,callback回調參數,參數內容爲http.ClientResponse的實例。 此外該函數http.request返回一個http.ClientRequest的實例,可向服務端發送數據內容,write以及end函數可調用。 http.get(options, callback) http 模塊還提供了一個更加簡便的方法用於處理GET請求:http.get。 它是http.request的簡化版,惟一的區別在於http.get自動將請求方法設爲了GET請求,同時不須要手動調用req.end();一樣的返回一個http.ClientRequest的實例。 http.ClientRequest實例對象,有response響應事件,也提供了write和end函數,用於向服務器發送請求體,一般用於POST、PUT等操做。 http.ClientResponse實例對象,也提供了三個事件data、end和close,分別在數據到達、傳輸結束和鏈接結束時觸發,此外還提供了setEncoding、pause、resume方法, 分別爲指定響應data事件的數據解碼方式、暫停接收數據和發送事件、從暫停的狀態中恢復。 10. 模塊加載 node加載模塊經過require加載,可加載內置核心模塊和外部文件(文件夾)模塊(依次以js/json/node文件),內置核心模塊具備最高優先權,當外部模塊和內置模塊命名衝突時,其將加載核心模塊。 文件模塊的加載有兩種方式,一種是按路徑加載模塊,一種是查找當前路徑下node_modules文件夾下對應命名的模塊。 若是require參數不以「 / 」、「 ./ 」或「 ../ 」開頭,而該模塊又不是核心模塊,那麼就要經過查找node_modules加載模塊,此時如require('express')即可代替require('./node_modules/express')。 此時在當前目錄下的 node_modules 目錄中來查找是否是有這樣一個模塊。若是沒有找到,則會在當前目錄的上一層中的 node_modules 目錄中繼續查找,反覆執行這一過程,直到遇到根目錄爲止。 之因此採起這個查找流程,主要是由於依賴模塊可能依賴於其餘被依賴的模塊,便於查找公共依賴模塊,以上溯查找到。 Node.js模塊不會被重複加載,這是由於Node.js經過文件名緩存全部加載過的文件模塊,因此之後再訪問到時就不會從新加載了。 require(some_module) 時的加載順序: 1. 若some_module是一個核心模塊,直接加載,結束。 2. 若some_module以「 / 」、「 ./ 」或「 ../ 」開頭,按路徑加載some_module,結束。 3. 若當前目錄爲current_dir,按路徑加載current_dir/node_modules/some_module。若是加載成功,結束,若是加載失敗,令current_dir爲其父目錄,回溯,重複這一過程, 直到遇到根目錄,拋出異常,結束。 對於不一樣的文件擴展名,加載處理方式不一樣,js文件則是fs模塊加載同步讀取文件後編譯執行,node文件(C/C++擴展)則用process.dlopen等加載擴展, json文件則是fs模塊加載同步讀取文件後用JSON.parse解析返回結果,其他文件擴展名的則按照js文件的處理方式同樣。 JSON文件在用做項目的配置文件時比較有用,經過require引入便可而不須要單獨用fs模塊來讀取,此外還有模塊緩存的方便。 內置核心模塊分爲:C/C++實現的模塊位於Node項目源碼的src目錄,JavaScript實現的模塊則放置於lib目錄下。 11. 控制流 Node的異步操做的事件式編程容易將程序的邏輯變得混亂。 異步機制是由事件和回調函數實現的,其容易形成回調循環陷阱(主要是js的做用域對象使用方式錯誤),以及深層的回調函數嵌套,難以弄清回調函數之間的關係。 有一些手段來下降回調的複雜度、耦合度以實現可讀的代碼,如:async控制流解耦模塊、streamlinejs和jscex以同步的方式編寫代碼,而在執行時異步實現、 或者eventproxy,其實現了對事件發射器的深度封裝,採用一種徹底基於事件鬆散耦合的方式來實現控制流的梳理。不過這些方法也帶來了一些複雜度。 12. Node不適合的場景 1. CPU計算密集型的任務。Node單線程基於事件,CPU佔用過多則會影響其餘請求響應,對於計算密集型任務可交給其餘服務器的其餘進程或專門進程處理,而後返還給服務器 並在適時發給客戶端。 2. 單用戶多任務型應用。Node單線程,須要利用多核資源時只能經過多進程的方式通訊處理任務,而多進程相互協做時比較麻煩。 3. 邏輯十分複雜的事務。Node基於事件、異步回調機制,其實現方式並非線性的,對於複雜的邏輯處理會比較困難,難以實現和維護, Node.js更善於處理那些邏輯簡單但訪問頻繁的任務。 4. Node.js不支持完整的Unicode,不少字符沒法用string表示。這不是Node.js的缺陷,而是JavaScript標準的問題,在使用中不少時候把全部的字符看成二進制的Buffer 數據來處理。對於中文支持,則須要:1. 保證JS文件是以UTF-8格式保存。2. JS文件中的writeHead方法中加入內容類型"charset=utf-8"編碼。