Express 實戰(二):Node.js 基礎

在上一篇文章中,咱們簡單的介紹了 Node.js 。瞭解到它基於 JavaScript、天生異步、擁有大量的第三方類庫。本文將會在以前的基礎上,對 Node.js 進行更深刻的介紹。其中主要內容包括:html

  • Node 的安裝
  • 如何使用第三方模塊生態
  • 第三方模塊的安裝
  • 一些簡單的使用示例
  • 開發過程當中的一些建議和技巧

在此以前,我假設你已經掌握了 JavaScript 基礎知識而且熟悉一些基本的命令行操做。另外,不要臆想經過這一章就全面掌握 Node。可是若是你有心的話,能夠去閱讀 Node.js 實戰前端

安裝Node

JavaScript 世界的一大特色就是它選擇性很是多,Node 的安裝也不例外。node

能夠在官方下載頁面找到各類版本的源代碼和安裝包文件。建議你使用與本身操做系統對應的安裝包進行安裝。固然,你也可用使用 apt-get、Homebrew 等包管理器進行安裝,若是你係統有的話。具體詳見官方的包管理工具的安裝指南git

若是你使用的是 Mac 或者 Linux 的話,那麼我極力推薦你使用 NVM 來安裝。Window 系統上的對應程序是 NVMW。這些版本管理工具,讓你能夠在不一樣版本間進行自由切換。例如,你能夠在嘗試新版本的特性時,同時在系統中保留一份穩定版。另外,NVM 無需系統管理權限同時卸載也很是容易。而安裝過程也只需在終端執行一行命令。程序員

如今,請在你係統中安裝好 Node。github

運行你的第一個Node腳本

安裝完成後,先動手寫個 "Hello World" 來檢驗一些。在新建的 helloworld.js 中加入一下代碼:web

console.log("Hello, World!");複製代碼

代碼中主要就是使用 console.log 來打印字符串 "Hello,world!",相信對於前端程序員來講並不會感到陌生。下面咱們使用 node helloworld.js 運行代碼。若是一切正常的話,會出現以下輸出:正則表達式

02_01
02_01

模塊的使用

在大多數編程語言中,咱們都會對代碼進行拆分,而後在使用的時候將這些文件引入其中。例如,C 和 C++ 中的 include,Python 的 import ,Ruby 和 PHP 中的 require。而另一些語言,如 C# 是在編譯時完成跨文件引用的。npm

很長一段時間內,JavaScript 官方並不支持模塊機制。因此社區中有人就編寫了 RequireJS 這種工具來解決依賴項導入的問題。可是,大多數時候仍是經過 \ 標籤來進行文件導入。而Node 經過實現名爲 CommonJS 的標準模塊,完美的解決了模塊導入問題。編程

模塊系統部分主要有三大主要內容:內置模塊的引入,第三方模塊引入,我的私有模塊引入。下面,將會對這些內容逐一介紹。

引入內置模塊

Node 已經內置了不少實用模塊,例如,文件系統模塊 fs,工具函數模塊 util

在 Node 編寫的 Web 應用中,最多見的任務當屬 URL 解析了。瀏覽器經過特定的 URL 來請求服務器上對應的資源。例如,訪問主頁、訪問關於頁面 的網絡請求。這些 URL 都以字符串的形式存在,咱們須要對其進行解析而後獲取更多的信息。這裏咱們經過對 URL 進行解析來介紹如何引入內置模塊。

內置的 url 模塊中暴露的方法很少,不過其中有一個 parse 函數很是有用。它能從 URL 字符串中提取到相似域名和路徑等有益信息。

這裏咱們使用 require 來實現模塊導入,該命令與以前提到的 Include、Import 的做用一致。經過將模塊名做爲參數,該命令就能成功的返回對應的模塊。大多數狀況下,該返回的對象是一個 object 對象,但有時也可能會是字符串、數字、或者函數。下面是引入改模塊的示例代碼:

var url = require("url");   
var parsedURL = url.parse("http://www.example.com/profile?name=barry");  

console.log(parsedURL.protocol);  // "http:"
console.log(parsedURL.host);       // "www.example.com"
console.log(parsedURL.query);     // "name=barry複製代碼

在上面的代碼中,經過 require("url") 返回一個模塊對象,而後就能夠像使用其餘對象同樣調用對象的方法。將這段代碼保存到 url-test.js 中並使運行 node url-test.js 命令,你就會看到協議名,域名、查詢條件。

另外,絕大多數時候咱們在引入模塊的時候會用一個同名的變量來接受返回的模塊對象。例如,上面就使用 url 來介紹 require("url") 的返回值。固然,你徹底能夠不遵循上面的規則。若是你想的話,你也能夠這麼幹:

var theURLModule = require("url");   
var parsedURL = theURLModule.parse("http://www.example.com/profile?name=barry");複製代碼

保存變量名和模塊名一致只是一個統一風格增長可讀性的寬鬆約定,而不是什麼強制規範。

使用 npm 和 package.json 引入第三方模塊

Node 的內置模塊遠遠不能知足平常開發須要,因此引入第三方模塊是一個必需要掌握的技能。

首先,咱們須要瞭解 package.json 文件。全部的 Node 項目都單獨存放在一個文件夾中,而項目若是使用了第三方模塊,那麼其中一定存在一個名爲 package.json 的文件。package.json 中的內容很是的簡單,通常其中定義了項目名稱、版本號、做者,已經項目的外部依賴項。

在新建的 Node 工程文件夾中,將下面的內容複製到 package.json 中。

{
  "name": "my-fun-project",   
  "author": "Evan Hahn",      
  "private": true,            
  "version": "0.2.0",         
  "dependencies": {}          
}複製代碼

其實,在進行 Node 安裝時實際上還安裝了另外一個程序:npm 。一般 npm 都被稱爲 Node 包管理器,而這也是它最大的特點。假設,如今須要在應用中導入一個小型的標準模版系統 Mustache。它能將模版字符串轉化爲真正的字符串,請看代碼:

// Returns "Hello, Nicholas Cage!"
Mustache.render("Hello, {{first}} {{last}}!", {
  first: "Nicholas",
  last: "Cage"
});

// Returns "Hello, Sheryl Sandberg!"
Mustache.render("Hello, {{first}} {{last}}!", {
  first: "Sheryl",
  last: "Sandberg"
});複製代碼

如今,假設你想經過 Mustache 模塊來編寫一個簡單的 Node 應用來歡迎 Nicolas Cage。

首先,在工程文件夾的根目錄裏運行 npm install mustache --save 。該命令會新建一個 node_modules 文件夾並將 Mustache 保存到文件夾下。 --save 參數將會把該模塊添加到 pakage.json 文件中。此時 pakage.json 文件夾大體以下,其中 Mustache 會使用最新的版本。

{
  "name": "my-fun-project",
  "author": "Evan Hahn",
  "private": true,
  "version": "0.2.0",
  "dependencies": {
    "mustache": "^2.0.0"  #A
  }
}複製代碼

若是你沒有使用 --save 選項的話,雖然也會建立 node_modules 文件夾將把 Mustache 模塊保存到同名子目錄下,可是 pakage.json 將不會發生任何變化。這裏之因此將這些依賴關係保存到 package.json 是爲了方便其餘開發者在獲得工程後直接使用 npm install 完成全部依賴項的安裝。另外一個緣由是 Node 項目在進行代碼管理時一般都會忽略 node_modules 文件夾而只保留 package.json。

安裝完成後接下來就是使用了:

var Mustache = require("mustache");  
var result = Mustache.render("Hi, {{first}} {{last}}!", {
  first: "Nicolas",
  last: "Cage"
});
console.log(result);複製代碼

保存代碼到 mustache-test.js 中並執行 node mustache-test.js 命令。而後你將會看見 Hi,Nicolas Cage! 。

就是這樣簡單,這些依賴項安裝完成後,你能夠像使用內置模塊同樣進行調用。node_modules 中模塊引入的工做直接交給 Node 就好了,你無需擔憂。

固然你能夠手動添加工程依賴項,而且你還能夠指定依賴項的版本。

npm init
除了安裝依賴項以外,npm 還能完成其餘任務。例如,自動生成 package.json 而不是經過手動編輯的方式。在一個新工程的文件夾中能夠經過 npm init 來配置工程名、做者、版本等信息,而後 npm 就會自定生成對應的 package.json 文件。這種自動化過程能夠節約開發者的時間。

實現私有模塊

前面都是介紹如何使用他人開發好的模塊,接下來你將會學到如何去開發一個私有模塊。假設如今須要隨機返回 0 ~ 100 之間的整數。在不引入其餘模塊的狀況下,代碼大體以下:

var MAX = 100;
function randomInteger() {
    return Math.floor( (Math.random() * MAX) );
}複製代碼

這可能與你在瀏覽器環境下代碼差很少,並無什麼特別之處。可是在 Node 中,咱們還須要暴露一個變量給外部使用。這樣當其餘程序在經過 require 進行引入的時候就能得到該變量。此例中,咱們暴露函數 randomInteger 並將代碼保存到 random-integer.js 文件中。

var MAX = 100;
function randomInteger() {
    return Math.floor( (Math.random() * MAX) );
}

module.exports = randomInteger;複製代碼

最後一行代碼對於 Node 初學者來講可能感受有點陌生。每一個模塊只能暴露一個變量,並且必須經過 module.exports 設置。本例中只暴露了一個函數變量,因此 MAX 就做爲模塊私有變量沒法被其餘文件所訪問。

module.exports 能夠暴露任何變量,雖然本例中是一個函數,可是一般都會是一個對象。固然,你能夠暴露字符串或者數組。

接下來咱們就來使用一下這個新模塊。在 random-integer.js 同一目錄下,新建一個 print-three-random-integers.js 並複製下面的代碼:

var randomInt = require("./random-integer");  #A
console.log(randomInt());  // 12
console.log(randomInt());  // 77
console.log(randomInt());  // 8複製代碼

除了須要經過點語法指定相對路徑以外,其他部分與前面幾乎一摸同樣。經過 node print-three-random-integers.js 命令,咱們能夠檢查程序的運行效果。不出意外的話,將會有三個 0 ~ 100 之間的隨機數會被打印出來。

若是你嘗試運行 node random-integer.js 的話,你還發現並無任何事情發生。雖然,咱們暴露了模塊中的函數,可是改函數並不會執行更不會打印任何輸出。

注意,這裏只涉及了私有模塊在工程中的使用。若是你但願將本身的模塊發佈出去供其餘人使用的話,能夠去個人我的站點查看相關內容。

以上部分就是 Node 模塊系統的簡單入門。

Node:異步的世界

在第一章中,我用 「烤鬆餅」 的例子簡單的介紹了 Node 中的異步特性。其中的關鍵點就是,你沒法同時作兩件事哪怕它們是同時發生的。雖然,在烘焙過程當中我能夠健身,可是,烤箱畢竟只是個外部事物。

02_02
02_02

Node 的異步工做原理與此相似,例如,你經過瀏覽器請求 Node 服務器上的一張小貓圖片。由於該圖片資源太大,因此在進行磁盤讀寫的時候你能夠抽身去處理其餘事情。此時,這個磁盤就至關於一個外部資源,咱們能夠直接處理第二個請求而無需掛起等待費時操做結束。

Express 中主要有兩個外部資源:

  1. 涉及文件系統。例如,磁盤文件的讀寫。
  2. 涉及網絡處理。例如,接受請求、發送響應。

在 Node 代碼中,這些異步都是經過回調進行處理的。其工做原理和在 Web 頁面發送 AJAX 請求同樣。在發送請求時你會附帶一個回調函數,當請求處理完成後你的回調將會被執行。

例如,如今你正在硬盤上讀取文件 myfile.txt 。當讀取結束後,你但願可以打印出其中字母 X 出現的次數,代碼以下:

var fs = require("fs");  

var options = { encoding: "utf-8" };                      
fs.readFile("myfile.txt", options, function(err, data) {  
  if (err) {                                
    console.error("Error reading file!");   
    return;                                 
  }                                        

  console.log(data.match(/x/gi).length + " letter X's");  
});複製代碼

下面咱們一步步解釋這些代碼:

首先,咱們導入 Node 自帶的文件系統模塊。該模塊主要處理文件相關內容,其中大多數都是文件讀寫功能。本例使用的其中的 readFile 方法。

接下來,咱們須要設置 fs.readFile 方法中的參數,第一個是文件名,第二個就是會回調函數。而且在讀取結束後執行回調函數。

在 Node 中大多數回調函數都會設置錯誤信息 error 做爲第一個參數。正常狀況下該參數等於 null ,若是出現錯誤則該參數會保存錯誤信息。雖然有時候這些錯誤信息並不會致使程序終止執行,可是多數情形下咱們都須要對錯誤作出響應,例如,拋出異常並跳出回調函數。這也是 Node 中最多見的回調實踐。

最後,當一切正常時咱們使用正則表達式匹配字母 X 並打印其數量。

下面咱們就來作個測試。這裏,咱們在上面代碼的結束加上一段,那麼會發生什麼事情呢?

var fs = require("fs");  

var options = { encoding: "utf-8" };                      
fs.readFile("myfile.txt", options, function(err, data) {  
  if (err) {                                
    console.error("Error reading file!");   
    return;                                 
  }                                        

  console.log(data.match(/x/gi).length + " letter X's");  
});

console.log("Hello World!");複製代碼

異步文件讀取時異步操做,因此這裏先打印出來的是 " Hello world! ",而後纔是異步函數中的打印操做。

這就是異步模式強大的地方。當一個外部設備在處理費時操做時,你能夠繼續運行其餘代碼。在 Web 應用中這意味着相同的時間能夠處理更多的請求。

注意:若是你想了解更多 JavaScript 異步的內容的話,你能夠去油管上查看這個視頻。視頻中的講解同時適用於 Node 和瀏覽器環境。

用 Node 構建 Web 服務:http 模塊

只有理解了上面那些概念,你才能更好的掌握 Node 內置的 HTTP 模塊。而該模塊對 Express 框架來講又是最重要的模塊之一。Node 和 Express 可以構建 Web 服務正是依賴於這個模塊中的功能。

Node 的 HTTP 模塊有不少特性(好比,向其餘服務器發送網絡請求),不過咱們將要使用的是其中一個名爲 http.createServer 的方法。該方法經過其回調函數來處理每一次的網絡請求,而且進行響應。下面代碼中咱們將全部的響應都設置爲了 "hello world" (能夠保存到 myserver.js 中)。

var http = require("http");           

function requestHandler(request, response) {             
  console.log("In comes a request to: " + request.url);  
  response.end("Hello, world!");                         
}                                                        

var server = http.createServer(requestHandler);  
server.listen(3000);複製代碼

上面的代碼由 4 個部分構成。

首先,咱們引入 HTTP 模塊並將其保存到變量 http 中。這與以前 URL 模塊的操做一致。

接着,定義了一個請求處理函數 requestHandler 。教程中的幾乎全部的代碼要麼是請求處理函數要麼是調用處理函數。該函數有兩個參數,request 表示請求對象,而 response 則表示響應對象。request 中包含 URL 路徑、user-agent 等信息。而經過調用 response 對象方法 Node 會將響應信息打包好併發送給請求者。

餘下的代碼則是指定內置的 HTTP 服務在請求是執行的處理函數以及服務監聽的端口號。

對於 HTTPS 來講,咱們則可使用自帶的 HTTPS 模塊。除了須要配置 SSL 證書,其他的過程都同樣。若是你瞭解 HTTPS 的話那麼後期從 HTTP 切換到 HTTPS 兩分鐘就能搞定。即便你不瞭解,也沒必要太過擔憂。

若是你將代碼保存到 myserver.js 並執行 node myserver.js 拉起服務。那麼,此時你在瀏覽器中訪問 http://localhost:3000 ,你就會看到:

02_03
02_03

你可能也注意到了,每當你發起請求的時候終端控制檯都會打印一些信息。當你嘗試訪問不一樣 URL 時,雖然控制檯打印的信息不一樣可是獲得的響應卻都是 「Hello, world!」。控制檯打印的信息相似於:

02_04
02_04

請注意上面打印的 URL 信息中並不包含 localhost:3000。雖然看起來顯得不那麼直觀,可是反過來這也是對的。畢竟使用相對路徑,咱們無需修改就能在任何電腦上部署 Node 應用。

而 URL 解析的代碼大體以下:

function requestHandler(req, res) {
    if (req.url === "/") {
        res.end("Welcome to the homepage!");
    } else if (req.url === "/about") {
        res.end("Welcome to the about page!");
    } else {
        res.end("Error! File not found.");
    }
}複製代碼

全部的請求 URL 均可以在這個函數裏面完成處理。這樣作對於簡單的應用來講確實很是簡單,可是當應用規模變大以後該函數就會變的臃腫不利於維護。這也是 Express 框架出現的重要緣由。

##總結
本文主要內容:

  • Node 的安裝
  • 模塊系統的使用
  • package.json 文件的介紹
  • 經過 package.json 安裝第三放模塊依賴項
  • Node 中的異步編程概念。
  • 簡單 HTTP 服務應用的建立。

原文地址

相關文章
相關標籤/搜索