做爲還在漫漫前端學習路上的一位自學者。我以學習分享的方式來整理本身對於知識的理解,同時也但願可以給你們做爲一份參考。但願可以和你們共同進步,若有任何紕漏的話,但願你們多多指正。感謝萬分!前端
在 Node.js 系列的第一節裏, 我會先介紹 Node.js 的一些基本概念. 讓你在看完這篇文章時, 能對 "什麼是 Node.js?", "爲何用 Node.js?" 這兩個問題有個基本的概念. 而且嘗試寫下第一句 Node.js 代碼.node
先來看看 Node 官網 給的答案:git
Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。 Node.js 使用了一個事件驅動、非阻塞式 I/O 的模型,使其輕量又高效。程序員
基本上, 不少人第一次看到上面這段定義的時候, 徹底搞不懂它在說什麼...github
下面我就來逐步解釋 Node.js 的這段定義.數據庫
看到這句話後, 你可能第一個疑問是 🤔 "什麼是 Chrome V8 引擎?"瀏覽器
在解答這個問題以前, 回到咱們熟悉的前端領域, 我先問另外一個問題 🤔 "JavaScript 代碼是如何在瀏覽器中運行的?"服務器
簡單來講, 咱們所寫的 JavaScript 源代碼, 是爲了給人看的, 瀏覽器加載 JavaScript 代碼後, 它是看不懂的, 須要翻譯成機器碼, 也就是 "機器的語言", 纔可讓機器執行. 在瀏覽器中, JavaScript 引擎負責進行代碼的解釋. V8 引擎是 Chrome 瀏覽器在用的 JavaScript 引擎. V8 引擎由 C++ 實現, 支持跨平臺, 就是說能夠在各類操做系統上使用, Node.js 基於 V8 引擎, 意味着它可讓 JavaScript 代碼脫離瀏覽器的束縛, 在各類各樣不一樣的操做系統上運行.微信
那麼一句話總結, Node.js 不是一門語言, 是一個能夠讓 JavaScript 代碼在各類各樣的平臺上獲得執行的運行環境. 之因此支持 JavaScript 語法是由於基於 V8 引擎.網絡
"非阻塞式 I/O", 我知道這幾個字看起來讓人有點懵. 彆着急, 那讓我換個說法, "不會阻塞 JavaScript 程序執行的 Input/Output 操做", 這樣會不會清楚一點? 可能你仍是不太懂, 那下面我就逐字地解釋.
在說 "非阻塞" 以前, 先了解什麼是 "阻塞". 從字面上理解, "阻塞" 就是堵住了, 通不過的意思. 在計算機中, 程序在線程內按順序被執行, 後面的操做必須等前面的操做結束才能被執行. 若是前一個操做耗時很長, 後面的操做就要一直等着, 直到前面的操做完成. 這個等待的狀態, 叫作 "阻塞".
線程: 程序執行流的最小單元。程序代碼在其中被 CPU 依次處理. 在一條線程中, 同一時間, 只有一段代碼被被執行, 或等待被執行.
如今再來講說什麼是 "Input/Output 操做". 從字面上翻譯就是 "輸入/輸出", 那輸的是什麼呢? 簡單說就是 "信息". 在程序執行過程當中若是須要操做系統進行 磁盤讀寫 或 網絡通訊, 咱們就都統稱爲 "I/O 操做". 例如, 從服務器獲取頁面, 寫入文件, 提交表單, 讀取數據庫都是屬於這個範疇的. 凡是這一類操做, 直觀感覺是 "花的時間較長". 那麼在代碼執行 I/O 操做時, 每每要讓後面的操做等很長時間. 整條線程一直處於阻塞的狀態, 這種 I/O 操做方式稱爲 "阻塞式 I/O". 拿生活舉例, 遊戲沒下載完, 我就只能乾等着玩不了.
在程序設計中, 對於高併發的任務 (同時發生的任務) , 在線程阻塞的狀況下, 整條線程不能執行程序, 這會致使任務處理速度極慢. 常見的方案是經過多線程來解決. 但每條線程的利用率並無增長, 同時也會致使硬件成本高昂.
併發: 在同一個時間段中, 幾個任務同時進行. 可是在任意時間點, 有且只有一個任務在執行.
(能夠想象成你在同時寫五份做業, 每份只寫幾筆就換下一個, 而後再反回來接着寫兩筆. 雖然五份做業在一段時間內同時都被寫了, 可是在任一時間點你只是在寫其中的一份做業.)
在 Node.js 中, JavaScript 代碼在一個單一的主線程中進行處理, 同一時間只能處理一項任務. 爲了處理高併發, 採用了 "非阻塞式 I/O", 也能夠稱爲 "異步式 I/O". 當線程遇到 I/O 操做時,不會以阻塞的方式去等待操做完成. 而是將 I/O 操做先放到另外一個地方等待處理, 而後 Node.js 繼續執行下一條代碼. 等主線程代碼所有執行完畢後, 再去處理 I/O 操做. 具體的過程, 在下一段介紹.
繼續上一段的內容. 在 Node.js 中, 當主線程遇到 I/O 操做時,會先把 I/O 操做封裝成一個請求對象, 而後被推入到線程池中等待執行. 而後異步調用的第一階段結束, JavaScript 主線程繼續執行後續操做, 直至全部非阻塞操做都處理完. 由於 I/O 操做在線程池中等處處理, 因此也不會阻塞主線程.
當線程池有空餘線程時, 等待的 I/O 操做會被處理. 當 I/O 操做完成, 獲取的結果保存在請求對象的 result
屬性上. 操做系統會來檢查線程池是否有處理完的請求, 若是有, 就把請求對象加入到事件隊列中.
在主線程的全部非阻塞代碼都處理完後, 事件循環開始逐個處理事件隊列中的事件. 保存在請求對象的 result
屬性被取出, 做爲與請求綁定的回調函數的參數. 而後執行回調來完成此 I/O 操做.
當隊列中已經沒有未處理的事件了, 程序結束. 能夠看出程序的結束與否, 取決於事件是否被所有處理完畢, 所以稱 Node 是事件驅動的.
若是很差理解的話, 能夠想象老王一我的去釣魚.
先來講 Node.js 的優勢:
固然 Node.js 也是有缺點的:
根據計算機平臺的不一樣, 安裝方法也略有差別. 本文側重於概念, 具體實操, 可能每位讀者因偏好不一樣, 方法各異. 在此就再也不贅述了.
最方便的方式是直接上官網去下載對應平臺的源碼或安裝包, 或者經過包管理器直接安裝.
var http = require('http');
function myNodeServer(req, res){
res.writeHead(200, {'Content-type':'text/plain'});
res.write('Hello World');
res.end();
}
http.createServer(myNodeServer).listen(3000); //監聽 3000 端口
console.log('Server is running!');
複製代碼
建立一個空文件, 把上面代碼複製到其中, 文件後綴改爲 .js
. 而後在命令行中, 用 node
命令執行剛剛的文件 ( 注意文件所在目錄位置和文件名 ) .
若是運行成功你的命令行上會顯示 "Server is running!" 這段話. 而後用瀏覽器訪問 http://localhost:3000/
這個地址. 你會看到網頁上顯示 "Hello World". 至此你就實現了一個簡易 HTTP 服務器.
😆 好啦,今天的分享就告一段落啦。下一篇中,我會介紹 Node.js 的模塊機制。
傳送門 - Node.js 系列 - 模塊機制
若是喜歡的話就點個關注吧!O(∩_∩)O 謝謝各位的支持❗️