如何正確學習Node

frank26發表於2018-02-24

0 :Node.js簡介

現在,越來越多的科技公司和開發者開始使用 Node.js 開發各種應用。Node.js除了能夠輔助大前端開發外,還可以編寫Web應用,封裝Api,組裝RPC服務等,甚至是開發VSCode編輯器一樣的PC客戶端。和其它技術相比, Node.js 簡單易學,效能好、部署容易,能夠輕鬆處理高併發場景下的大量伺服器請求。Node.js 周邊的生態也非常強大,NPM(Node包管理)上有超過60萬個模組,日下超過載量3億次。但編寫 Node.js 程式碼對新人和其它語言背景的開發者來說,不是一件容易的事,在入門之前需要弄懂不少複雜的概念。

a)Node.js簡介

  • 優點: Node.js 不是一門語言也不是框架,它只是基於 Google V8 引擎的 JavaScript 執行時環境,同時結合 Libuv 擴充套件了 JavaScript 功能,使之支援 io、fs 等只有語言才有的特性,使得 JavaScript 能夠同時具有 DOM 操作(瀏覽器)和 I/O、檔案讀寫、運算元據庫(伺服器端)等能力,是目前最簡單的全棧式語言。

早在2007年,Jeff Atwood 就提出了著名的 Atwood定律

任何能夠用 JavaScript 實現的應用系統,最終都必將用 JavaScript 實現

  • 缺點:

當然了,Node.js 也有一些缺點。Node.js 經常被人們吐槽的一點就是:回撥太多難於控制(俗稱回撥地獄)和 CPU 密集任務處理的不是很好。但是,目前非同步流程技術已經取得了非常不錯的進步,從Callback、Promise 到 Async函式,可以輕鬆的滿足所有開發需求。至於 CPU 密集任務處理並非不可解,方案有很多,比如通過系統底層語言 Rust 來擴充套件 Node.js,但這樣會比較麻煩。筆者堅信在合適的場景使用合適的東西,尤其是在微服務架構下,一切都是服務,可以做到語言無關。如果大家想使 JavaScript 做 CPU 密集任務,推薦 Node.js 的兄弟專案 fibjs,基於纖程(fiber,可以簡單理解為更輕量級的執行緒),效率非常高,相容npm,同時沒有非同步回撥煩惱。 b)什麼是Node.js?

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.

  • Node.js 不是 JavaScript 應用,不是語言(JavaScript 是語言),不是像 Rails(Ruby)、 Laravel(PHP) 或 Django(Python) 一樣的框架,也不是像 Nginx 一樣的 Web 伺服器。Node.js 是 JavaScript 執行時環境
  • 構建在 Chrome's V8 這個著名的 JavaScript 引擎之上,Chrome V8 引擎以 C/C++ 為主,相當於使用JavaScript 寫法,轉成 C/C++ 呼叫,大大的降低了學習成本
  • 事件驅動(event-driven),非阻塞 I/O 模型(non-blocking I/O model),簡單點講就是每個函式都是非同步的,最後由 Libuv 這個 C/C++ 編寫的事件迴圈處理庫來處理這些 I/O 操作,隱藏了非阻塞 I/O 的具體細節,簡化併發程式設計模型,讓你可以輕鬆的編寫高效能的Web應用,所以它是輕量(lightweight)且高效(efficient)的
  • 使用 npm 作為包管理器,目前 npm 是開源庫裡包管理最大的生態,功能強大,截止到2017年12月,模組數量超過 60 萬+
  • 應用: 大多數人都認為 Node.js 只能寫網站後臺或者前端工具,這其實是不全面的,Node.js的目標是讓併發程式設計更簡單,主要應用在以網路程式設計為主的 I/O 密集型應用。它是開源的,跨平臺,並且高效(尤其是I/O處理),包括IBM、Microsoft、Yahoo、SAP、PayPal、沃爾瑪及GoDaddy都是 Node.js 的使用者。

c)基本原理

下面是一張 Node.js 早期的架構圖,來自 Node.js 之父 Ryan Dahl 的演講稿,在今天依然不過時,它簡要的介紹了 Node.js 是基於 Chrome V8引擎構建的,由事件迴圈(Event Loop)分發 I/O 任務,最終工作執行緒(Work Thread)將任務丟到執行緒池(Thread Pool)裡去執行,而事件迴圈只要等待執行結果就可以了。

如何正確學習Node
如何正確學習Node

核心概念

  • Chrome V8 是 Google 釋出的開源 JavaScript 引擎,採用 C/C++ 編寫,在 Google 的 Chrome 瀏覽器中被使用。Chrome V8 引擎可以獨立執行,也可以用來嵌入到 C/C++ 應用程式中執行。
  • Event Loop 事件迴圈(由 libuv 提供)
  • Thread Pool 執行緒池(由 libuv 提供)

梳理一下

  • Chrome V8 是 JavaScript 引擎
  • Node.js 內建 Chrome V8 引擎,所以它使用的 JavaScript 語法
  • JavaScript 語言的一大特點就是單執行緒,也就是說,同一個時間只能做一件事
  • 單執行緒就意味著,所有任務需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務耗時很長,後一個任務就不得不一直等著。
  • 如果排隊是因為計算量大,CPU 忙不過來,倒也算了,但是很多時候 CPU 是閒著的,因為 I/O 很慢,不得不等著結果出來,再往下執行
  • CPU 完全可以不管 I/O 裝置,掛起處於等待中的任務,先執行排在後面的任務
  • 將等待中的 I/O 任務放到 Event Loop 裡
  • 由 Event Loop 將 I/O 任務放到執行緒池裡
  • 只要有資源,就盡力執行
  • Chrome V8 解釋並執行 JavaScript 程式碼(這就是為什麼瀏覽器能執行 JavaScript 原因)
  • libuv 由事件迴圈和執行緒池組成,負責所有 I/O 任務的分發與執行

1. 前言:學習 Node.js 的三個境界

  • 打日誌:console.log
  • 斷點除錯:斷點除錯:node debugger 或node inspector 或vscode
  • 測試驅動開發(tdd | bdd)

2. 準備與學習:

基礎學習

1)js語法必須會

  1. js基本語法,都是c語系的,有其他語言背景學習起來相對更簡單
  2. 常見用法,比如正則,比如資料結構,尤其是陣列的幾種用法。比如bind/call/apply等等
  3. 物件導向寫法。js是基於物件的,所以它的oo寫起來非常詭異。參見紅皮書JavaScript高階程式設計,很多框架都是自己實現oo基礎框架,比如ext-core等。

2)個人學習和技術選型都要循序漸進

  1. 先能寫,採用程式導向寫法,簡單理解就是定義一堆function,然後呼叫,非常簡單
  2. 然後再追求更好的寫法,可以物件導向。對於規模化的程式設計來說,oo是有它的優勢的,一般java、c#,ruby這些語言裡都有物件導向,所以後端更習慣,但對於語言經驗不那麼強的前端來說算高階技巧。
  3. 等oo玩膩了,可以有更好的追求:函數語言程式設計,無論程式設計思維,還是用法上都對已有的程式設計思維是個挑戰。我很喜歡函式式,但不太會在團隊裡使用,畢竟oo階段還沒完全掌握,風險會比較大。但如果團隊水平都非常高了,團隊穩定是可以用的。

可以看出我的思路,先能寫,然後再追求更好的寫法,比如物件導向。等團隊水平到一定程度了,並且穩定的時候,可以考慮更加極致的函式式寫法。

3)各種高階的JavaScript友好語言

JavaScript友好語言指的是能夠使用其他語法實現,但最終編譯成js的語言。自從Node.js出現後,這種黑科技層出不窮。比如比較有名的coffee、typescript、babel(es)等。

CoffeeScript雖然也是JavaScript友好語言,但其語法借鑑ruby,崇尚極簡,對於型別和OO機制上還是偏弱,而且這麼多年也沒發展起來,仍然是比較小眾的活著。未來比例會越來越少的。

顯然TypeScript會越來越好,TypeScript 的強大之處是要用過才知道的。

  • 1)規模化程式設計,像Java那種,靜態型別,物件導向,前端只有TypeScript能做到
  • 2)親爹是微軟安德斯·海爾斯伯格,不知道此人的請看borland傳奇去
  • 3)開源,未來很好
  • 4)組合拳:TypeScript + VSCode = 神器

當下前端發展速度極快,以指數級的曲線增長。以前可能1年都不一定有一項新技術,現在可能每個月都有。大前端,Node全棧,架構演進等等都在快速變化。可以說,前端越複雜,有越多的不確定性,TypeScript的機會就越大。 4)再論物件導向

物件導向想用好也不容易的,而且js裡有各種實現,真是讓人眼花繚亂。

  • 基於原型的寫法,縱觀JavaScript高階程式設計,就是翻來覆去的講這個,這個很基礎,但不好是很好用。可以不用,但不可以不會。
  • 自己寫物件導向機制是最好的,但不是每個人都有這個能力的。好在es6規範出了更好一點的物件導向,通過class、extends、super關鍵字來定義類,已經明顯好很多了,雖然還很弱,但起碼勉強能用起來了。從程式導向走過來的同學,推薦這種寫法,簡單易用。但要注意物件導向要有物件導向的寫法,要理解抽象,繼承,封裝,多型4個基本特徵。如果想用好,你甚至還需要看一些設計模式相關的書。好在有《JavaScript設計模式》一書。Koa2裡已經在用這種寫法了。
  • js是指令碼語言,解釋即可執行。所以它的最大缺點是沒有型別系統,這在規模化程式設計裡是非常危險的,一個函式,傳參就能玩死人。於是現在流行使用flow和typescript來做型別校驗。flow只是工具,比較輕量級。而typescript是es6超級,給es6補充了型別系統和更完善的物件導向機制,所以大部分人都會對ts有好感,很有可能是未來的趨勢。

Node.js應用場景

《Node.js in action》一書裡說,Node.js 所針對的應用程式有一個專門的簡稱:DIRT。它表示資料密集型實時(data-intensive real-time)程式。因為 Node.js 自身在 I/O 上非常輕量,它善於將資料從一個管道混排或代理到另一個管道上,這能在處理大量請求時持有很多開放的連線,並且只佔用一小部分記憶體。它的設計目標是保證響應能力,跟瀏覽器一樣。

這話不假,但在今天來看,DIRT 還是範圍小了。其實 DIRT 本質上說的 I/O 處理的都算,但隨著大前端的發展,Node.js 已經不再只是 I/O 處理相關,而是更加的“Node”!

如何正確學習Node

  • 1)跨平臺:覆蓋你能想到的面向使用者的所有平臺,傳統的PC Web端,以及PC客戶端 nw.js/electron 、移動端 cordova、HTML5、react-nativeweex,硬體 ruff.io
  • 2)Web應用開發:網站、Api、RPC服務等
  • 3)前端:三大框架 React \ Vue \ Angular 輔助開發,以及工程化演進過程(使用Gulp /Webpack 構建 Web 開發工具)
  • 4)工具:npm上各種工具模組,包括各種前端預編譯、構建工具 Grunt / Gulp、腳手架,命令列工具,各種奇技淫巧等

Node.js 應用場景非常豐富,比如 Node.js 可以開發作業系統,但一般我都不講的,就算說了也沒多大意義,難道大家真的會用嗎?一般,我習慣將 Node.js 應用場景氛圍7個部分。

1)初衷,server端,不想成了前端開發的基礎設施 2)命令列輔助工具,甚至可以是運維 3)移動端:cordova,pc端:nw.js和electron 4)元件化,構建,代理 5)架構,前後端分離、api proxy 6)效能優化、反爬蟲與爬蟲 7) 全棧最便捷之路

Node核心:非同步流程控制

Node.js是為非同步而生的,它自己把複雜的事兒做了(高併發,低延時),交給使用者的只是有點難用的Callback寫法。也正是坦誠的將非同步回撥暴露出來,才有更好的流程控制方面的演進。也正是這些演進,讓Node.js從DIRT(資料敏感實時應用)擴充套件到更多的應用場景,今天的Node.js已經不只是能寫後端的JavaScript,已經涵蓋了所有涉及到開發的各個方面,而Node全棧更是熱門種的熱門。

直面問題才能有更好的解決方式,Node.js的非同步是整個學習Node.js過程中重中之重。

    1. 非同步流程控制學習重點
  • 2)Api寫法:Error-first Callback 和 EventEmitter
  • 3)中流砥柱:Promise
  • 4)終極解決方案:Async/Await
  1. 非同步流程控制學習重點

我整理了一張圖,更直觀一些。從09年到現在,8年多的時間裡,整個Node.js社群做了大量嘗試,其中曲折足足夠寫一本書的了。大家先簡單瞭解一下。

如何正確學習Node

  • 紅色代表Promise,是使用最多的,無論async還是generator都可用
  • 藍色是Generator,過度貨
  • 綠色是Async函式,趨勢

結論:Promise是必須會的,那你為什麼不順勢而為呢?

推薦:使用Async函式 + Promise組合,如下圖所示。

結論

  1. Node.js SDK裡callback寫法必須會的。
  2. Node.js學習重點: Async函式與Promise
    1. 中流砥柱:Promise
    2. 終極解決方案:Async/Await

如何正確學習Node
2)Api寫法:Error-first Callback 和 EventEmitter a)Error-first Callback 定義錯誤優先的回撥寫法只需要注意2條規則即可:

  • 回撥函式的第一個引數返回的error物件,如果error發生了,它會作為第一個err引數返回,如果沒有,一般做法是返回null。
  • 回撥函式的第二個引數返回的是任何成功響應的結果資料。如果結果正常,沒有error發生,err會被設定為null,並在第二個引數就出返回成功結果資料。

下面讓我們看一下呼叫函式示例,Node.js 文件裡最常採用下面這樣的回撥方式:

function(err, res) {
  // process the error and result
}
複製程式碼

這裡的 callback 指的是帶有2個引數的函式:"err"和 "res"。語義上講,非空的“err”相當於程式異常;而空的“err”相當於可以正常返回結果“res”,無任何異常。 b)EventEmitter

事件模組是 Node.js 內建的對觀察者模式“釋出/訂閱”(publish/subscribe)的實現,通過EventEmitter屬性,提供了一個建構函式。該建構函式的例項具有 on 方法,可以用來監聽指定事件,並觸發回撥函式。任意物件都可以釋出指定事件,被 EventEmitter 例項的 on 方法監聽到。

在node 6之後,可以直接使用require('events')

var EventEmitter = require('events')
var util = require('util')

var MyEmitter = function () {
 
}
util.inherits(MyEmitter, EventEmitter)

const myEmitter = new MyEmitter();

myEmitter.on('event', (a, b) => {
  console.log(a, b, this);
    // Prints: a b {}
});

myEmitter.emit('event', 'a', 'b');
複製程式碼

和jquery、vue裡的Event是非常類似的。而且前端自己也有EventEmitter。 c)如何更好的查Node.js文件

API是應用程式介面Application Programming Interface的簡稱。從Node.js非同步原理,我們可以知道,核心在於 Node.js SDK 中API呼叫,然後交由EventLoop(Libuv)去執行,所以我們一定要熟悉Node.js的API操作。

Node.js的API都是非同步的,同步的函式是奢求,要查API文件,在高併發場景下慎用。

筆者推薦使用 DashZeal 檢視離線文件,經常檢視離線文件,對Api理解會深入很多,比IDE輔助要好,可以有效避免離開IDE就不會寫程式碼的窘境。 3)中流砥柱:Promise 回撥地獄

Node.js 因為採用了錯誤優先的回撥風格寫法,導致sdk裡匯出都是回撥函式。如果組合呼叫的話,就會特別痛苦,經常會出現回撥裡巢狀回撥的問題,大家都非常厭煩這種寫法,稱之為Callback Hell,即回撥地獄。一個經典的例子來自著名的Promise模組q文件裡。

step1(function (value1) {
    step2(value1, function(value2) {
        step3(value2, function(value3) {
            step4(value3, function(value4) {
                // Do something with value4
            });
        });
    });
});
複製程式碼

這裡只是做4步,巢狀了4層回撥,如果更多步驟呢?很多新手淺嘗輒止,到這兒就望而卻步,粉轉黑。這明顯不夠成熟,最起碼你要看看它的應對解決方案吧! Node.js 約定所有Api都採用錯誤優先的回撥方式,這部分場景都是大家直接呼叫介面,無太多變化。而Promise是對回撥地獄的思考,或者說是改良方案。目前使用非常普遍,可以說是在async函式普及之前唯一一個通用性規範,甚至 Node.js 社群都在考慮 Promise 化,可見其影響之大。

Promise最早也是在commonjs社群提出來的,當時提出了很多規範。比較接受的是promise/A規範。後來人們在這個基礎上,提出了promise/A+規範,也就是實際上現在的業內推行的規範。ES6 也是採用的這種規範。 Promise意味著[許願|承諾]一個還沒有完成的操作,但在未來會完成的。與Promise最主要的互動方法是通過將函式傳入它的then方法從而獲取得Promise最終的值或Promise最終最拒絕(reject)的原因。要點有三個:

  • 遞迴,每個非同步操作返回的都是promise物件

  • 狀態機:三種狀態轉換,只在promise物件內部可以控制,外部不能改變狀態

  • 全域性異常處理 Async函式要點如下:

  • Async函式語義上非常好

  • Async不需要執行器,它本身具備執行能力,不像Generator需要co模組

  • Async函式的異常處理採用try/catch和Promise的錯誤處理,非常強大

  • Await接Promise,Promise自身就足夠應對所有流程了,包括async函式沒有純並行處理機制,也可以採用Promise裡的all和race來補齊

  • Await釋放Promise的組合能力,外加co和Promise的then,幾乎沒有不支援的場景

綜上所述

  • Async函式是趨勢,如果Chrome 52. v8 5.1已經支援Async函式(https://github.com/nodejs/CTC/issues/7)了,Node.js支援還會遠麼?
  • Async和Generator函式裡都支援promise,所以promise是必須會的。
  • Generator和yield異常強大,不過不會成為主流,所以學會基本用法和promise就好了,沒必要所有的都必須會。
  • co作為Generator執行器是不錯的,它更好的是當做Promise 包裝器,通過Generator支援yieldable,最後返回Promise,是不是有點無恥?
    1. 非同步流程控制學習重點
  • 2)Api寫法:Error-first Callback 和 EventEmitter
  • 3)中流砥柱:Promise
  • 4)終極解決方案:Async/Await

Web程式設計要點

一般,後端開發指的是 Web 應用開發中和檢視渲染無關的部分,主要是和資料庫互動為主的重業務型邏輯處理。但現在架構升級後,Node.js 承擔了前後端分離重任之後,有了更多玩法。從帶檢視的傳統Web應用面向Api介面應用,到通過 RPC 呼叫封裝對資料庫的操作,到提供前端 Api 代理和閘道器,服務組裝等,統稱為後端開發,不再是以往只有和資料庫打交道的部分才算後端。這樣,就可以讓前端工程師對開發過程可控,更好的進行調優和效能優化。 對 Node.js 來說,一直沒有在後端取得其合理的佔有率,原因是多方面的,暫列幾條。

  • 1)利益分配,已有實現大多是Java或者其他語言,基本是沒法撼動的,重寫的成本是巨大的,另外,如果用Node寫了,那麼那些寫Java的人怎麼辦?搶人飯碗,這是要拼命的。
  • 2)Node相對年輕,大家對Node的理解不夠,回撥和非同步流程控制略麻煩,很多架構師都不願意花時間去學習。儘管在Web應用部分處理起來非常簡單高效,但在遇到問題時並不容易排查定位,對開發者水平要求略高。
  • 3)開發者技能單一,很多是從前端轉過來的,對資料庫,架構方面知識欠缺,對系統設計也知之不多,這是很危險的,有種麻桿打狼兩頭害怕的感覺。
  • 4)Node在科普、培訓、佈道等方面做的並不好,國外使用的非常多,國內卻很少人知道,不如某些語言做得好。

儘管如此,Node.js 還是盡人皆知,捲入各種是非風口,也算是在大前端浪潮中大紅大紫。原因它的定位非常明確,補足以 JavaScript 為核心的全棧體系中伺服器部分。開發也是人,能夠同時掌握並精通多門語言的人畢竟不多,而且程式設計師的美德是“懶”,能使用 JavaScript 一門語言完成所有事兒,為什麼要學更多呢? 我們可以根據框架的特性進行分類

框架名稱 特性 點評
Express 簡單、實用,路由中介軟體等五臟俱全 最著名的Web框架
Derby.js && Meteor 同構 前後端都放到一起,模糊了開發便捷,看上去更簡單,實際上上對開發來說要求更高
Sails、Total 面向其他語言,Ruby、PHP等 借鑑業界優秀實現,也是 Node.js 成熟的一個標誌
MEAN.js 面向架構 類似於腳手架,又期望同構,結果只是蹭了熱點
Hapi和Restfy 面向Api && 微服務 移動網際網路時代Api的作用被放大,故而獨立分類。尤其是對於微服務開發更是利器
ThinkJS 面向新特性 借鑑ThinkPHP,並慢慢走出自己的一條路,對於Async函式等新特性支援,無出其右,新版v3.0是基於Koa v2.0的作為核心的
Koa 專注於非同步流程改進 下一代Web框架
Egg 基於Koa,在開發上有極大便利 企業級Web開發框架

對於框架選型

  • 業務場景、特點,不必為了什麼而什麼,避免本末倒置
  • 自身團隊能力、喜好,有時候技術選型決定團隊氛圍的,需要平衡激進與穩定
  • 出現問題的時候,有人能夠做到原始碼級定製。Node.js 已經有8年曆史,但模組完善程度良莠不齊,如果不慎踩到一個坑裡,需要團隊在無外力的情況能夠搞定,否則會影響進度

Tips:個人學習求新,企業架構求穩,無非喜好與場景而已 Web程式設計核心

  • 非同步流程控制(前面講過了)

  • 基本框架 Koa或Express,新手推薦Express,畢竟資料多,上手更容易。如果有一定經驗,推薦Koa,其實這些都是為了瞭解Web程式設計原理,尤其是中介軟體機制理解。

  • 資料庫 mongodb或mysql都行,mongoose和Sequelize、bookshelf,TypeOrm等都非常不錯。對於事物,不是Node.js的鍋,是你選的資料庫的問題。另外一些偏門,想node連sqlserver等估計還不成熟,我是不會這樣用的。

  • 模板引擎, ejs,jade,nunjucks。理解原理最好。尤其是extend,include等高階用法,理解佈局,複用的好處。其實前後端思路都是一樣的。

  • Node 的書幾乎都過時了,我該買哪本?

  • Node 用途那麼多,我該從哪裡學起?

  • Node Web 框架那麼多,我該怎麼選?

相關文章