node.js入門教程

1、Node.js 安裝配置

 Node.js安裝包及源碼下載地址爲:https://nodejs.org/en/download/javascript

Node.js 歷史版本下載地址:https://nodejs.org/dist/php

注意:Linux上安裝Node.js須要安裝Python 2.6 或 2.7 ,不建議安裝Python 3.0以上版本。html

1.1 Windows 安裝包(.msi)

32 位安裝包下載地址 : https://nodejs.org/dist/v4.4.3/node-v4.4.3-x86.msijava

64 位安裝包下載地址 : https://nodejs.org/dist/v4.4.3/node-v4.4.3-x64.msinode

本文實例以 v0.10.26 版本爲例,其餘版本相似, 安裝步驟:python

步驟 1 : 雙擊下載後的安裝包 v0.10.26,以下所示:mysql

 

步驟 2 : 點擊以上的Run(運行),將出現以下界面:linux

 

步驟 3 : 勾選接受協議選項,點擊 next(下一步) 按鈕 :git

 

步驟 4 : Node.js默認安裝目錄爲 "C:\Program Files\nodejs\" , 你能夠修改目錄,並點擊 next(下一步):github

 

步驟 5 : 點擊樹形圖標來選擇你須要的安裝模式 , 而後點擊下一步 next(下一步)

 

步驟 6 :點擊 Install(安裝) 開始安裝Node.js。你也能夠點擊 Back(返回)來修改先前的配置。 而後並點擊 next(下一步):

 

安裝過程:

 

點擊 Finish(完成)按鈕退出安裝嚮導。

 

檢測PATH環境變量是否配置了Node.js,點擊開始=》運行=》輸入"cmd" => 輸入命令"path",輸出以下結果:

PATH=C:\oraclexe\app\oracle\product\10.2.0\server\bin;C:\Windows\system32;

C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;

c:\python32\python;C:\MinGW\bin;C:\Program Files\GTK2-Runtime\lib;

C:\Program Files\MySQL\MySQL Server 5.5\bin;C:\Program Files\nodejs\;

C:\Users\rg\AppData\Roaming\npm

咱們能夠看到環境變量中已經包含了C:\Program Files\nodejs\

檢查Node.js版本

 

 

2、Node.js 建立第一個應用

2.1組成部分

  1. 引入 required 模塊:咱們可使用 require 指令來載入 Node.js 模塊。
  2. 建立服務器:服務器能夠監聽客戶端的請求,相似於 Apache 、Nginx 等 HTTP 服務器。
  3. 接收請求與響應請求 服務器很容易建立,客戶端可使用瀏覽器或終端發送 HTTP 請求,服務器接收請求後返回響應數據。

 

2.2 建立 Node.js 應用

步驟1、引入 required 模塊

咱們使用 require 指令來載入 http 模塊,並將實例化的 HTTP 賦值給變量 http,實例以下:

var http = require("http");

步驟2、建立服務器

接下來咱們使用 http.createServer() 方法建立服務器,並使用 listen 方法綁定 8888 端口。 函數經過 request, response 參數來接收和響應數據。

實例以下,在你項目的根目錄下建立一個叫 server.js 的文件,並寫入如下代碼:

var http = require('http');
 
http.createServer(function (request, response) {
 
    // 發送 HTTP 頭部 
    // HTTP 狀態值: 200 : OK
    // 內容類型: text/plain
    response.writeHead(200, {'Content-Type': 'text/plain'});
 
    // 發送響應數據 "Hello World"
    response.end('Hello World\n');
}).listen(8888);
 
// 終端打印以下信息
console.log('Server running at http://127.0.0.1:8888/');

以上代碼咱們完成了一個能夠工做的 HTTP 服務器。

使用 node 命令執行以上的代碼:

node server.js
Server running at http://127.0.0.1:8888/

 

接下來,打開瀏覽器訪問 http://127.0.0.1:8888/,你會看到一個寫着 "Hello World"的網頁。

 

分析Node.js HTTP 服務器:

  • 第一行請求(require)Node.js 自帶的 http 模塊,而且把它賦值給 http 變量。
  • 接下來咱們調用 http 模塊提供的函數: createServer 。這個函數會返回 一個對象,這個對象有一個叫作 listen 的方法,這個方法有一個數值參數, 指定這個 HTTP 服務器監聽的端口號。

3、NPM 使用介紹

NPM是隨同NodeJS一塊兒安裝的包管理工具,能解決NodeJS代碼部署上的不少問題,常見的使用場景有如下幾種:

  • 容許用戶從NPM服務器下載別人編寫的第三方包到本地使用。
  • 容許用戶從NPM服務器下載並安裝別人編寫的命令行程序到本地使用。
  • 容許用戶將本身編寫的包或命令行程序上傳到NPM服務器供別人使用。

因爲新版的nodejs已經集成了npm,因此以前npm也一併安裝好了。一樣能夠經過輸入 "npm -v" 來測試是否成功安裝。命令以下,出現版本提示表示安裝成功:

$ npm -v
2.3.0

若是你安裝的是舊版本的 npm,能夠很容易得經過 npm 命令來升級,命令以下:

$ sudo npm install npm -g
/usr/local/bin/npm -> /usr/local/lib/node_modules/npm/bin/npm-cli.js
npm@2.14.2 /usr/local/lib/node_modules/npm

若是是 Window 系統使用如下命令便可:

npm install npm -g

使用淘寶鏡像的命令:

cnpm install npm -g

3.1使用 npm 命令安裝模塊

npm 安裝 Node.js 模塊語法格式以下:

$ npm install <Module Name>

如下實例,咱們使用 npm 命令安裝經常使用的 Node.js web框架模塊 express:

$ npm install express

安裝好以後,express 包就放在了工程目錄下的 node_modules 目錄中,所以在代碼中只須要經過 require('express') 的方式就好,無需指定第三方包路徑。

var express = require('express');

3.2全局安裝與本地安裝

npm 的包安裝分爲本地安裝(local)、全局安裝(global)兩種,從敲的命令行來看,差異只是有沒有-g而已,好比

npm install express          # 本地安裝
npm install express -g   # 全局安裝

若是出現如下錯誤:

npm err! Error: connect ECONNREFUSED 127.0.0.1:8087 

解決辦法爲:

$ npm config set proxy null

本地安裝

  • 1. 將安裝包放在 ./node_modules 下(運行 npm 命令時所在的目錄),若是沒有 node_modules 目錄,會在當前執行 npm 命令的目錄下生成 node_modules 目錄。
  • 2. 能夠經過 require() 來引入本地安裝的包。
  • 1. 將安裝包放在 /usr/local 下或者你 node 的安裝目錄。
  • 2. 能夠直接在命令行裏使用。

全局安裝

若是你但願具有二者功能,則須要在兩個地方安裝它或使用 npm link

接下來咱們使用全局方式安裝 express

$ npm install express -g

安裝過程輸出以下內容,第一行輸出了模塊的版本號及安裝位置。

express@4.13.3 node_modules/express
├── escape-html@1.0.2
├── range-parser@1.0.2
├── merge-descriptors@1.0.0
├── array-flatten@1.1.1
├── cookie@0.1.3
├── utils-merge@1.0.0
├── parseurl@1.3.0
├── cookie-signature@1.0.6
├── methods@1.1.1
├── fresh@0.3.0
├── vary@1.0.1
├── path-to-regexp@0.1.7
├── content-type@1.0.1
├── etag@1.7.0
├── serve-static@1.10.0
├── content-disposition@0.5.0
├── depd@1.0.1
├── qs@4.0.0
├── finalhandler@0.4.0 (unpipe@1.0.0)
├── on-finished@2.3.0 (ee-first@1.1.1)
├── proxy-addr@1.0.8 (forwarded@0.1.0, ipaddr.js@1.0.1)
├── debug@2.2.0 (ms@0.7.1)
├── type-is@1.6.8 (media-typer@0.3.0, mime-types@2.1.6)
├── accepts@1.2.12 (negotiator@0.5.3, mime-types@2.1.6)
└── send@0.13.0 (destroy@1.0.3, statuses@1.2.1, ms@0.7.1, mime@1.3.4, http-errors@1.3.1)

查看安裝信息

你可使用如下命令來查看全部全局安裝的模塊:

$ npm list -g
 
├─┬ cnpm@4.3.2
│ ├── auto-correct@1.0.0
│ ├── bagpipe@0.3.5
│ ├── colors@1.1.2
│ ├─┬ commander@2.9.0
│ │ └── graceful-readlink@1.0.1
│ ├─┬ cross-spawn@0.2.9
│ │ └── lru-cache@2.7.3
……

若是要查看某個模塊的版本號,可使用命令以下:

$ npm list grunt
 
projectName@projectVersion /path/to/project/folder
└── grunt@0.4.1

3.3使用 package.json

package.json 位於模塊的目錄下,用於定義包的屬性。接下來讓咱們來看下 express 包的 package.json 文件,位於 node_modules/express/package.json 內容:

{
  "name": "express",
  "description": "Fast, unopinionated, minimalist web framework",
  "version": "4.13.3",
  "author": {
    "name": "TJ Holowaychuk",
    "email": "tj@vision-media.ca"
  },
  "contributors": [
    {
      "name": "Aaron Heckmann",
      "email": "aaron.heckmann+github@gmail.com"
    },
    {
      "name": "Ciaran Jessup",
      "email": "ciaranj@gmail.com"
    },
    {
      "name": "Douglas Christopher Wilson",
      "email": "doug@somethingdoug.com"
    },
    {
      "name": "Guillermo Rauch",
      "email": "rauchg@gmail.com"
    },
    {
      "name": "Jonathan Ong",
      "email": "me@jongleberry.com"
    },
    {
      "name": "Roman Shtylman",
      "email": "shtylman+expressjs@gmail.com"
    },
    {
      "name": "Young Jae Sim",
      "email": "hanul@hanul.me"
    }
  ],
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/strongloop/express.git"
  },
  "homepage": "http://expressjs.com/",
  "keywords": [
    "express",
    "framework",
    "sinatra",
    "web",
    "rest",
    "restful",
    "router",
    "app",
    "api"
  ],
  "dependencies": {
    "accepts": "~1.2.12",
    "array-flatten": "1.1.1",
    "content-disposition": "0.5.0",
    "content-type": "~1.0.1",
    "cookie": "0.1.3",
    "cookie-signature": "1.0.6",
    "debug": "~2.2.0",
    "depd": "~1.0.1",
    "escape-html": "1.0.2",
    "etag": "~1.7.0",
    "finalhandler": "0.4.0",
    "fresh": "0.3.0",
    "merge-descriptors": "1.0.0",
    "methods": "~1.1.1",
    "on-finished": "~2.3.0",
    "parseurl": "~1.3.0",
    "path-to-regexp": "0.1.7",
    "proxy-addr": "~1.0.8",
    "qs": "4.0.0",
    "range-parser": "~1.0.2",
    "send": "0.13.0",
    "serve-static": "~1.10.0",
    "type-is": "~1.6.6",
    "utils-merge": "1.0.0",
    "vary": "~1.0.1"
  },
  "devDependencies": {
    "after": "0.8.1",
    "ejs": "2.3.3",
    "istanbul": "0.3.17",
    "marked": "0.3.5",
    "mocha": "2.2.5",
    "should": "7.0.2",
    "supertest": "1.0.1",
    "body-parser": "~1.13.3",
    "connect-redis": "~2.4.1",
    "cookie-parser": "~1.3.5",
    "cookie-session": "~1.2.0",
    "express-session": "~1.11.3",
    "jade": "~1.11.0",
    "method-override": "~2.3.5",
    "morgan": "~1.6.1",
    "multiparty": "~4.1.2",
    "vhost": "~3.0.1"
  },
  "engines": {
    "node": ">= 0.10.0"
  },
  "files": [
    "LICENSE",
    "History.md",
    "Readme.md",
    "index.js",
    "lib/"
  ],
  "scripts": {
    "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
    "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
    "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
    "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
  },
  "gitHead": "ef7ad681b245fba023843ce94f6bcb8e275bbb8e",
  "bugs": {
    "url": "https://github.com/strongloop/express/issues"
  },
  "_id": "express@4.13.3",
  "_shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3",
  "_from": "express@*",
  "_npmVersion": "1.4.28",
  "_npmUser": {
    "name": "dougwilson",
    "email": "doug@somethingdoug.com"
  },
  "maintainers": [
    {
      "name": "tjholowaychuk",
      "email": "tj@vision-media.ca"
    },
    {
      "name": "jongleberry",
      "email": "jonathanrichardong@gmail.com"
    },
    {
      "name": "dougwilson",
      "email": "doug@somethingdoug.com"
    },
    {
      "name": "rfeng",
      "email": "enjoyjava@gmail.com"
    },
    {
      "name": "aredridel",
      "email": "aredridel@dinhe.net"
    },
    {
      "name": "strongloop",
      "email": "callback@strongloop.com"
    },
    {
      "name": "defunctzombie",
      "email": "shtylman@gmail.com"
    }
  ],
  "dist": {
    "shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3",
    "tarball": "http://registry.npmjs.org/express/-/express-4.13.3.tgz"
  },
  "directories": {},
  "_resolved": "https://registry.npmjs.org/express/-/express-4.13.3.tgz",
  "readme": "ERROR: No README data found!"
}

Package.json 屬性說明

  • name - 包名。
  • version - 包的版本號。
  • description - 包的描述。
  • homepage - 包的官網 url 。
  • author - 包的做者姓名。
  • contributors - 包的其餘貢獻者姓名。
  • dependencies - 依賴包列表。若是依賴包沒有安裝,npm 會自動將依賴包安裝在 node_module 目錄下。
  • repository - 包代碼存放的地方的類型,能夠是 git 或 svn,git 可在 Github 上。
  • main - main 字段指定了程序的主入口文件,require('moduleName') 就會加載這個文件。這個字段的默認值是模塊根目錄下面的 index.js。
  • keywords - 關鍵字

3.4卸載模塊

咱們可使用如下命令來卸載 Node.js 模塊。

$ npm uninstall express

卸載後,你能夠到 /node_modules/ 目錄下查看包是否還存在,或者使用如下命令查看:

$ npm ls

3.5更新模塊

咱們可使用如下命令更新模塊:

$ npm update express

3.6搜索模塊

使用如下來搜索模塊:

$ npm search express

3.7建立模塊

建立模塊,package.json 文件是必不可少的。咱們可使用 NPM 生成 package.json 文件,生成的文件包含了基本的結果。

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
 
See `npm help json` for definitive documentation on these fields
and exactly what they do.
 
Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.
 
Press ^C at any time to quit.
name: (node_modules) runoob                   # 模塊名
version: (1.0.0) 
description: Node.js 測試模塊(www.runoob.com)  # 描述
entry point: (index.js) 
test command: make test
git repository: https://github.com/runoob/runoob.git  # Github 地址
keywords: 
author: 
license: (ISC) 
About to write to ……/node_modules/package.json:      # 生成地址
 
{
  "name": "runoob",
  "version": "1.0.0",
  "description": "Node.js 測試模塊(www.runoob.com)",
  ……
}
 
 
Is this ok? (yes) yes

以上的信息,你須要根據你本身的狀況輸入。在最後輸入 "yes" 後會生成 package.json 文件。

接下來咱們可使用如下命令在 npm 資源庫中註冊用戶(使用郵箱註冊):

$ npm adduser
Username: mcmohd
Password:
Email: (this IS public) mcmohd@gmail.com

接下來咱們就用如下命令來發布模塊:

$ npm publish

若是你以上的步驟都操做正確,你就能夠跟其餘模塊同樣使用 npm 來安裝。


3.8版本號

使用NPM下載和發佈代碼時都會接觸到版本號。NPM使用語義版本號來管理代碼,這裏簡單介紹一下。

語義版本號分爲X.Y.Z三位,分別表明主版本號、次版本號和補丁版本號。當代碼變動時,版本號按如下原則更新。

  • 若是隻是修復bug,須要更新Z位。
  • 若是是新增了功能,可是向下兼容,須要更新Y位。
  • 若是有大變更,向下不兼容,須要更新X位。

版本號有了這個保證後,在申明第三方包依賴時,除了可依賴於一個固定版本號外,還可依賴於某個範圍的版本號。例如"argv": "0.0.x"表示依賴於0.0.x系列的最新版argv。

NPM支持的全部版本號範圍指定方式能夠查看官方文檔


3.9 NPM 經常使用命令

除了本章介紹的部分外,NPM還提供了不少功能,package.json裏也有不少其它有用的字段。

除了能夠在npmjs.org/doc/查看官方文檔外,這裏再介紹一些NPM經常使用命令。

NPM提供了不少命令,例如install和publish,使用npm help可查看全部命令。

  • NPM提供了不少命令,例如installpublish,使用npm help可查看全部命令。
  • 使用npm help <command>可查看某條命令的詳細幫助,例如npm help install
  • package.json所在目錄下使用npm install . -g可先在本地安裝當前命令行程序,可用於發佈前的本地測試。
  • 使用npm update <package>能夠把當前目錄下node_modules子目錄裏邊的對應模塊更新至最新版本。
  • 使用npm update <package> -g能夠把全局安裝的對應命令行程序更新至最新版。
  • 使用npm cache clear能夠清空NPM本地緩存,用於對付使用相同版本號發佈新版本代碼的人。
  • 使用npm unpublish <package>@<version>能夠撤銷發佈本身發佈過的某個版本代碼。

3.10 使用淘寶 NPM 鏡像

你們都知道國內直接使用 npm 的官方鏡像是很是慢的,這裏推薦使用淘寶 NPM 鏡像。

淘寶 NPM 鏡像是一個完整 npmjs.org 鏡像,你能夠用此代替官方版本(只讀),同步頻率目前爲 10分鐘 一次以保證儘可能與官方服務同步。

你可使用淘寶定製的 cnpm (gzip 壓縮支持) 命令行工具代替默認的 npm:

$ npm install -g cnpm --registry=https://registry.npm.taobao.org

這樣就可使用 cnpm 命令來安裝模塊了:

$ cnpm install [name]

4、Node.js REPL(交互式解釋器)

Node.js REPL(Read Eval Print Loop:交互式解釋器) 表示一個電腦的環境,相似 Window 系統的終端或 Unix/Linux shell,咱們能夠在終端中輸入命令,並接收系統的響應。

Node 自帶了交互式解釋器,能夠執行如下任務:

  • 讀取 - 讀取用戶輸入,解析輸入了Javascript 數據結構並存儲在內存中。
  • 執行 - 執行輸入的數據結構
  • 打印 - 輸出結果
  • 循環 - 循環操做以上步驟直到用戶兩次按下 ctrl-c 按鈕退出。

Node 的交互式解釋器能夠很好的調試 Javascript 代碼。

開始學習 REPL

咱們能夠輸入如下命令來啓動 Node 的終端:

$ node
> 

這時咱們就能夠在 > 後輸入簡單的表達式,並按下回車鍵來計算結果。

4.1簡單的表達式運算

接下來讓咱們在 Node.js REPL 的命令行窗口中執行簡單的數學運算:

$ node
> 1 +4
5
> 5 / 2
2.5
> 3 * 6
18
> 4 - 1
3
> 1 + ( 2 * 3 ) - 4
3

4.2使用變量

你能夠將數據存儲在變量中,並在你須要的時候使用它。

變量聲明須要使用 var 關鍵字,若是沒有使用 var 關鍵字變量會直接打印出來。

使用 var 關鍵字的變量可使用 console.log() 來輸出變量。

$ node
> x = 10
10
> var y = 10
undefined
> x + y
20
> console.log("Hello World")
Hello World
undefined
> console.log("www.runoob.com")
www.runoob.com
undefined

4.3多行表達式

Node REPL 支持輸入多行表達式,這就有點相似 JavaScript。接下來讓咱們來執行一個 do-while 循環:

$ node
> var x = 0
undefined
> do {
... x++;
... console.log("x: " + x);
... } while ( x < 5 );
x: 1
x: 2
x: 3
x: 4
x: 5
undefined

... 三個點的符號是系統自動生成的,你回車換行後便可。Node 會自動檢測是否爲連續的表達式。

4.4下劃線(_)變量

你可使用下劃線(_)獲取上一個表達式的運算結果:

$ node
> var x = 10
undefined
> var y = 20
undefined
> x + y
30
> var sum = _
undefined
> console.log(sum)
30
undefined

4.5REPL 命令

  • ctrl + c - 退出當前終端。
  • ctrl + c 按下兩次 - 退出 Node REPL。
  • ctrl + d - 退出 Node REPL.
  • 向上/向下 - 查看輸入的歷史命令
  • tab  - 列出當前命令
  • .help - 列出使用命令
  • .break - 退出多行表達式
  • .clear - 退出多行表達式
  • .save filename - 保存當前的 Node REPL 會話到指定文件
  • .load filename - 載入當前 Node REPL 會話的文件內容。

4.6中止 REPL

前面咱們已經提到按下兩次 ctrl + c 鍵就能退出 REPL:

$ node
(^C again to quit)

5、Node.js 回調函數

Node.js 異步編程的直接體現就是回調。

異步編程依託於回調來實現,但不能說使用了回調後程序就異步化了。

回調函數在完成任務後就會被調用,Node 使用了大量的回調函數,Node 全部 API 都支持回調函數。

例如,咱們能夠一邊讀取文件,一邊執行其餘命令,在文件讀取完成後,咱們將文件內容做爲回調函數的參數返回。這樣在執行代碼時就沒有阻塞或等待文件 I/O 操做。這就大大提升了 Node.js 的性能,能夠處理大量的併發請求。


5.1阻塞代碼實例

建立一個文件 input.txt ,內容以下:

菜鳥教程官網地址:www.runoob.com

建立 main.js 文件, 代碼以下:

var fs = require("fs");
 
var data = fs.readFileSync('input.txt');
 
console.log(data.toString());
console.log("程序執行結束!");

以上代碼執行結果以下:

$ node main.js
菜鳥教程官網地址:www.runoob.com
 
程序執行結束!

5.2非阻塞代碼實例

建立一個文件 input.txt ,內容以下:

菜鳥教程官網地址:www.runoob.com

建立 main.js 文件, 代碼以下:

var fs = require("fs");
 
fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});
 
console.log("程序執行結束!");

以上代碼執行結果以下:

$ node main.js
程序執行結束!
菜鳥教程官網地址:www.runoob.com

以上兩個實例咱們瞭解了阻塞與非阻塞調用的不一樣。第一個實例在文件讀取完後才執行完程序。 第二個實例咱們不須要等待文件讀取完,這樣就能夠在讀取文件時同時執行接下來的代碼,大大提升了程序的性能。

所以,阻塞是按順序執行的,而非阻塞是不須要按順序的,因此若是須要處理回調函數的參數,咱們就須要寫在回調函數內。

6、Node.js 事件循環

Node.js 是單進程單線程應用程序,可是經過事件和回調支持併發,因此性能很是高。

Node.js 的每個 API 都是異步的,並做爲一個獨立線程運行,使用異步函數調用,並處理併發。

Node.js 基本上全部的事件機制都是用設計模式中觀察者模式實現。

Node.js 單線程相似進入一個while(true)的事件循環,直到沒有事件觀察者退出,每一個異步事件都生成一個事件觀察者,若是有事件發生就調用該回調函數.


6.1事件驅動程序

Node.js 使用事件驅動模型,當web server接收到請求,就把它關閉而後進行處理,而後去服務下一個web請求。

當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被返回給用戶。

這個模型很是高效可擴展性很是強,由於webserver一直接受請求而不等待任何讀寫操做。(這也被稱之爲非阻塞式IO或者事件驅動IO)

在事件驅動模型中,會生成一個主循環來監聽事件,當檢測到事件時觸發回調函數。

 

整個事件驅動的流程就是這麼實現的,很是簡潔。有點相似於觀察者模式,事件至關於一個主題(Subject),而全部註冊到這個事件上的處理函數至關於觀察者(Observer)。

Node.js 有多個內置的事件,咱們能夠經過引入 events 模塊,並經過實例化 EventEmitter 類來綁定和監聽事件,以下實例:

// 引入 events 模塊
var events = require('events');
// 建立 eventEmitter 對象
var eventEmitter = new events.EventEmitter();

如下程序綁定事件處理程序:

// 綁定事件及事件的處理程序
eventEmitter.on('eventName', eventHandler);

咱們能夠經過程序觸發事件:

// 觸發事件
eventEmitter.emit('eventName');

6.2實例

建立 main.js 文件,代碼以下所示:

// 引入 events 模塊
var events = require('events');
// 建立 eventEmitter 對象
var eventEmitter = new events.EventEmitter();
 
// 建立事件處理程序
var connectHandler = function connected() {
   console.log('鏈接成功。');
  
   // 觸發 data_received 事件 
   eventEmitter.emit('data_received');
}
 
// 綁定 connection 事件處理程序
eventEmitter.on('connection', connectHandler);
 
// 使用匿名函數綁定 data_received 事件
eventEmitter.on('data_received', function(){
   console.log('數據接收成功。');
});
 
// 觸發 connection 事件 
eventEmitter.emit('connection');
 
console.log("程序執行完畢。");

接下來讓咱們執行以上代碼:

$ node main.js
鏈接成功。
數據接收成功。
程序執行完畢。

7、Node.js EventEmitter

Node.js 全部的異步 I/O 操做在完成時都會發送一個事件到事件隊列。

Node.js裏面的許多對象都會分發事件:一個net.Server對象會在每次有新鏈接時分發一個事件, 一個fs.readStream對象會在文件被打開的時候發出一個事件。 全部這些產生事件的對象都是 events.EventEmitter 的實例。


7.1 EventEmitter 類

events 模塊只提供了一個對象: events.EventEmitter。EventEmitter 的核心就是事件觸發與事件監聽器功能的封裝。

你能夠經過require("events");來訪問該模塊。

// 引入 events 模塊
var events = require('events');
// 建立 eventEmitter 對象
var eventEmitter = new events.EventEmitter();

EventEmitter 對象若是在實例化時發生錯誤,會觸發 error 事件。當添加新的監聽器時,newListener 事件會觸發,當監聽器被移除時,removeListener 事件被觸發。

下面咱們用一個簡單的例子說明 EventEmitter 的用法:

//event.js 文件
var EventEmitter = require('events').EventEmitter; 
var event = new EventEmitter(); 
event.on('some_event', function() { 
    console.log('some_event 事件觸發'); 
}); 
setTimeout(function() { 
    event.emit('some_event'); 
}, 1000); 

執行結果以下:

運行這段代碼,1 秒後控制檯輸出了 'some_event 事件觸發'。其原理是 event 對象註冊了事件 some_event 的一個監聽器,而後咱們經過 setTimeout 在 1000 毫秒之後向 event 對象發送事件 some_event,此時會調用some_event 的監聽器。

$ node event.js 
some_event 事件觸發

EventEmitter 的每一個事件由一個事件名和若干個參數組成,事件名是一個字符串,一般表達必定的語義。對於每一個事件,EventEmitter 支持 若干個事件監聽器。

當事件觸發時,註冊到這個事件的事件監聽器被依次調用,事件參數做爲回調函數參數傳遞。

讓咱們如下面的例子解釋這個過程:

//event.js 文件
var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener1', arg1, arg2); 
}); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener2', arg1, arg2); 
}); 
emitter.emit('someEvent', 'arg1 參數', 'arg2 參數'); 

執行以上代碼,運行的結果以下:

$ node event.js 
listener1 arg1 參數 arg2 參數
listener2 arg1 參數 arg2 參數

以上例子中,emitter 爲事件 someEvent 註冊了兩個事件監聽器,而後觸發了 someEvent 事件。

運行結果中能夠看到兩個事件監聽器回調函數被前後調用。 這就是EventEmitter最簡單的用法。

EventEmitter 提供了多個屬性,如 on 和 emiton 函數用於綁定事件函數,emit 屬性用於觸發一個事件。接下來咱們來具體看下 EventEmitter 的屬性介紹。

方法

序號

方法 & 描述

1

addListener(event, listener)
爲指定事件添加一個監聽器到監聽器數組的尾部。

2

on(event, listener)
爲指定事件註冊一個監聽器,接受一個字符串 event 和一個回調函數。

server.on('connection', function (stream) {
  console.log('someone connected!');
});

3

once(event, listener)
爲指定事件註冊一個單次監聽器,即 監聽器最多隻會觸發一次,觸發後馬上解除該監聽器。

server.once('connection', function (stream) {
  console.log('Ah, we have our first user!');
});

4

removeListener(event, listener)

移除指定事件的某個監聽器,監聽器必須是該事件已經註冊過的監聽器。

它接受兩個參數,第一個是事件名稱,第二個是回調函數名稱。

var callback = function(stream) {
  console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);

5

removeAllListeners([event])
移除全部事件的全部監聽器, 若是指定事件,則移除指定事件的全部監聽器。

6

setMaxListeners(n)
默認狀況下, EventEmitters 若是你添加的監聽器超過 10 個就會輸出警告信息。 setMaxListeners 函數用於提升監聽器的默認限制的數量。

7

listeners(event)
返回指定事件的監聽器數組。

8

emit(event, [arg1], [arg2], [...])
按參數的順序執行每一個監聽器,若是事件有註冊監聽返回 true,不然返回 false。

類方法

序號

方法 & 描述

1

listenerCount(emitter, event)
返回指定事件的監聽器數量。

事件

序號

事件 & 描述

1

newListener

  • event - 字符串,事件名稱
  • listener - 處理事件函數

該事件在添加新監聽器時被觸發。

2

removeListener

  • event - 字符串,事件名稱
  • listener - 處理事件函數

從指定監聽器數組中刪除一個監聽器。須要注意的是,此操做將會改變處於被刪監聽器以後的那些監聽器的索引。

實例

如下實例經過 connection(鏈接)事件演示了 EventEmitter 類的應用。

建立 main.js 文件,代碼以下:

var events = require('events');
var eventEmitter = new events.EventEmitter();
 
// 監聽器 #1
var listener1 = function listener1() {
   console.log('監聽器 listener1 執行。');
}
 
// 監聽器 #2
var listener2 = function listener2() {
  console.log('監聽器 listener2 執行。');
}
 
// 綁定 connection 事件,處理函數爲 listener1 
eventEmitter.addListener('connection', listener1);
 
// 綁定 connection 事件,處理函數爲 listener2
eventEmitter.on('connection', listener2);
 
var eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 個監聽器監聽鏈接事件。");
 
// 處理 connection 事件 
eventEmitter.emit('connection');
 
// 移除監綁定的 listener1 函數
eventEmitter.removeListener('connection', listener1);
console.log("listener1 再也不受監聽。");
 
// 觸發鏈接事件
eventEmitter.emit('connection');
 
eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 個監聽器監聽鏈接事件。");
 
console.log("程序執行完畢。");

以上代碼,執行結果以下所示:

$ node main.js
2 個監聽器監聽鏈接事件。
監聽器 listener1 執行。
監聽器 listener2 執行。
listener1 再也不受監聽。
監聽器 listener2 執行。
1 個監聽器監聽鏈接事件。
程序執行完畢。

7.2 error 事件

EventEmitter 定義了一個特殊的事件 error,它包含了錯誤的語義,咱們在遇到 異常的時候一般會觸發 error 事件。

當 error 被觸發時,EventEmitter 規定若是沒有響 應的監聽器,Node.js 會把它看成異常,退出程序並輸出錯誤信息。

咱們通常要爲會觸發 error 事件的對象設置監聽器,避免遇到錯誤後整個程序崩潰。例如:

var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.emit('error'); 

運行時會顯示如下錯誤:

node.js:201 
throw e; // process.nextTick error, or 'error' event on first tick 
^ 
Error: Uncaught, unspecified 'error' event. 
at EventEmitter.emit (events.js:50:15) 
at Object.<anonymous> (/home/byvoid/error.js:5:9) 
at Module._compile (module.js:441:26) 
at Object..js (module.js:459:10) 
at Module.load (module.js:348:31) 
at Function._load (module.js:308:12) 
at Array.0 (module.js:479:10) 
at EventEmitter._tickCallback (node.js:192:40) 

7.3 繼承 EventEmitter

大多數時候咱們不會直接使用 EventEmitter,而是在對象中繼承它。包括 fs、net、 http 在內的,只要是支持事件響應的核心模塊都是 EventEmitter 的子類。

爲何要這樣作呢?緣由有兩點:

首先,具備某個實體功能的對象實現事件符合語義, 事件的監聽和發生應該是一個對象的方法。

其次 JavaScript 的對象機制是基於原型的,支持 部分多重繼承,繼承 EventEmitter 不會打亂對象原有的繼承關係。

八Node.js Buffer(緩衝區)

JavaScript 語言自身只有字符串數據類型,沒有二進制數據類型。

但在處理像TCP流或文件流時,必須使用到二進制數據。所以在 Node.js中,定義了一個 Buffer 類,該類用來建立一個專門存放二進制數據的緩存區。

在 Node.js 中,Buffer 類是隨 Node 內核一塊兒發佈的核心庫。Buffer 庫爲 Node.js 帶來了一種存儲原始數據的方法,可讓 Node.js 處理二進制數據,每當須要在 Node.js 中處理I/O操做中移動的數據時,就有可能使用 Buffer 庫。原始數據存儲在 Buffer 類的實例中。一個 Buffer 相似於一個整數數組,但它對應於 V8 堆內存以外的一塊原始內存。

v6.0以前建立Buffer對象直接使用new Buffer()構造函數來建立對象實例,可是Buffer對內存的權限操做相比很大,能夠直接捕獲一些敏感信息,因此在v6.0之後,官方文檔裏面建議使用 Buffer.from() 接口去建立Buffer對象。


8.1 Buffer 與字符編碼

Buffer 實例通常用於表示編碼字符的序列,好比 UTF-8 、 UCS2 、 Base64 、或十六進制編碼的數據。 經過使用顯式的字符編碼,就能夠在 Buffer 實例與普通的 JavaScript 字符串之間進行相互轉換。

const buf = Buffer.from('runoob', 'ascii');
 
// 輸出 72756e6f6f62
console.log(buf.toString('hex'));
 
// 輸出 cnVub29i
console.log(buf.toString('base64'));

Node.js 目前支持的字符編碼包括:

  • ascii - 僅支持 7 位 ASCII 數據。若是設置去掉高位的話,這種編碼是很是快的。
  • utf8 - 多字節編碼的 Unicode 字符。許多網頁和其餘文檔格式都使用 UTF-8 。
  • utf16le - 2 或 4 個字節,小字節序編碼的 Unicode 字符。支持代理對(U+10000 至 U+10FFFF)。
  • ucs2 - utf16le 的別名。
  • base64 - Base64 編碼。
  • latin1 - 一種把 Buffer 編碼成一字節編碼的字符串的方式。
  • binary - latin1 的別名。
  • hex - 將每一個字節編碼爲兩個十六進制字符。

8.2建立 Buffer 類

Buffer 提供瞭如下 API 來建立 Buffer 類:

  • Buffer.alloc(size[, fill[, encoding]]) 返回一個指定大小的 Buffer 實例,若是沒有設置 fill,則默認填滿 0
  • Buffer.allocUnsafe(size) 返回一個指定大小的 Buffer 實例,可是它不會被初始化,因此它可能包含敏感的數據
  • Buffer.allocUnsafeSlow(size)
  • Buffer.from(array) 返回一個被 array 的值初始化的新的 Buffer 實例(傳入的 array 的元素只能是數字,否則就會自動被 0 覆蓋)
  • Buffer.from(arrayBuffer[, byteOffset[, length]]) 返回一個新建的與給定的 ArrayBuffer 共享同一內存的 Buffer。
  • Buffer.from(buffer) 複製傳入的 Buffer 實例的數據,並返回一個新的 Buffer 實例
  • Buffer.from(string[, encoding]) 返回一個被 string 的值初始化的新的 Buffer 實例
// 建立一個長度爲 十、且用 0 填充的 Buffer。
const buf1 = Buffer.alloc(10);
 
// 建立一個長度爲 十、且用 0x1 填充的 Buffer。 
const buf2 = Buffer.alloc(10, 1);
 
// 建立一個長度爲 十、且未初始化的 Buffer。
// 這個方法比調用 Buffer.alloc() 更快,
// 但返回的 Buffer 實例可能包含舊數據,
// 所以須要使用 fill() 或 write() 重寫。
const buf3 = Buffer.allocUnsafe(10);
 
// 建立一個包含 [0x1, 0x2, 0x3] 的 Buffer。
const buf4 = Buffer.from([1, 2, 3]);
 
// 建立一個包含 UTF-8 字節 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
const buf5 = Buffer.from('tést');
 
// 建立一個包含 Latin-1 字節 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
const buf6 = Buffer.from('tést', 'latin1');

8.3寫入緩衝區

語法

寫入 Node 緩衝區的語法以下所示:

buf.write(string[, offset[, length]][, encoding])

參數

參數描述以下:

  • string - 寫入緩衝區的字符串。
  • offset - 緩衝區開始寫入的索引值,默認爲 0 。
  • length - 寫入的字節數,默認爲 buffer.length
  • encoding - 使用的編碼。默認爲 'utf8' 。

根據 encoding 的字符編碼寫入 string 到 buf 中的 offset 位置。 length 參數是寫入的字節數。 若是 buf 沒有足夠的空間保存整個字符串,則只會寫入 string 的一部分。 只部分解碼的字符不會被寫入。

返回值

返回實際寫入的大小。若是 buffer 空間不足, 則只會寫入部分字符串。

實例

buf = Buffer.alloc(256);
len = buf.write("www.runoob.com");
 
console.log("寫入字節數 : "+  len);

執行以上代碼,輸出結果爲:

$node main.js
寫入字節數 : 14

8.4從緩衝區讀取數據

語法

讀取 Node 緩衝區數據的語法以下所示:

buf.toString([encoding[, start[, end]]])

參數

參數描述以下:

  • encoding - 使用的編碼。默認爲 'utf8' 。
  • start - 指定開始讀取的索引位置,默認爲 0。
  • end - 結束位置,默認爲緩衝區的末尾。

返回值

解碼緩衝區數據並使用指定的編碼返回字符串。

實例

buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
  buf[i] = i + 97;
}
 
console.log( buf.toString('ascii'));       // 輸出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5));   // 輸出: abcde
console.log( buf.toString('utf8',0,5));    // 輸出: abcde
console.log( buf.toString(undefined,0,5)); // 使用 'utf8' 編碼, 並輸出: abcde

執行以上代碼,輸出結果爲:

$ node main.js
abcdefghijklmnopqrstuvwxyz
abcde
abcde
abcde

8.5將 Buffer 轉換爲 JSON 對象

語法

將 Node Buffer 轉換爲 JSON 對象的函數語法格式以下:

buf.toJSON()

當字符串化一個 Buffer 實例時,JSON.stringify() 會隱式地調用該 toJSON()

返回值

返回 JSON 對象。

實例

const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);
 
// 輸出: {"type":"Buffer","data":[1,2,3,4,5]}
console.log(json);
 
const copy = JSON.parse(json, (key, value) => {
  return value && value.type === 'Buffer' ?
    Buffer.from(value.data) :
    value;
});
 
// 輸出: <Buffer 01 02 03 04 05>
console.log(copy);

執行以上代碼,輸出結果爲:

{"type":"Buffer","data":[1,2,3,4,5]}
<Buffer 01 02 03 04 05>

8.6緩衝區合併

語法

Node 緩衝區合併的語法以下所示:

Buffer.concat(list[, totalLength])

參數

參數描述以下:

  • list - 用於合併的 Buffer 對象數組列表。
  • totalLength - 指定合併後Buffer對象的總長度。

返回值

返回一個多個成員合併的新 Buffer 對象。

實例

var buffer1 = Buffer.from(('菜鳥教程'));
var buffer2 = Buffer.from(('www.runoob.com'));
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log("buffer3 內容: " + buffer3.toString());

執行以上代碼,輸出結果爲:

buffer3 內容: 菜鳥教程 www.runoob.com

8.7緩衝區比較

語法

Node Buffer 比較的函數語法以下所示, 該方法在 Node.js v0.12.2 版本引入:

buf.compare(otherBuffer);

參數

參數描述以下:

  • otherBuffer - 與 buf 對象比較的另一個 Buffer 對象。

返回值

返回一個數字,表示 buf 在 otherBuffer 以前,以後或相同。

實例

var buffer1 = Buffer.from('ABC');
var buffer2 = Buffer.from('ABCD');
var result = buffer1.compare(buffer2);
 
if(result < 0) {
   console.log(buffer1 + " 在 " + buffer2 + "以前");
}else if(result == 0){
   console.log(buffer1 + " 與 " + buffer2 + "相同");
}else {
   console.log(buffer1 + " 在 " + buffer2 + "以後");
}

執行以上代碼,輸出結果爲:

ABC在ABCD以前

8.8拷貝緩衝區

語法

Node 緩衝區拷貝語法以下所示:

buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])

參數

參數描述以下:

  • targetBuffer - 要拷貝的 Buffer 對象。
  • targetStart - 數字, 可選, 默認: 0
  • sourceStart - 數字, 可選, 默認: 0
  • sourceEnd - 數字, 可選, 默認: buffer.length

返回值

沒有返回值。

實例

var buf1 = Buffer.from('abcdefghijkl');
var buf2 = Buffer.from('RUNOOB');
 
//將 buf2 插入到 buf1 指定位置上
buf2.copy(buf1, 2);
 
console.log(buf1.toString());

執行以上代碼,輸出結果爲:

abRUNOOBijkl

8.9緩衝區裁剪

Node 緩衝區裁剪語法以下所示:

buf.slice([start[, end]])

參數

參數描述以下:

  • start - 數字, 可選, 默認: 0
  • end - 數字, 可選, 默認: buffer.length

返回值

返回一個新的緩衝區,它和舊緩衝區指向同一塊內存,可是從索引 start 到 end 的位置剪切。

實例

var buffer1 = Buffer.from('runoob');
// 剪切緩衝區
var buffer2 = buffer1.slice(0,2);
console.log("buffer2 content: " + buffer2.toString());

執行以上代碼,輸出結果爲:

buffer2 content: ru

8.10緩衝區長度

語法

Node 緩衝區長度計算語法以下所示:

buf.length;

返回值

返回 Buffer 對象所佔據的內存長度。

實例

var buffer = Buffer.from('www.runoob.com');
//  緩衝區長度
console.log("buffer length: " + buffer.length);

執行以上代碼,輸出結果爲:

buffer length: 14

 

 

9、Node.js Stream(流)

Stream 是一個抽象接口,Node 中有不少對象實現了這個接口。例如,對http 服務器發起請求的request 對象就是一個 Stream,還有stdout(標準輸出)。

Node.js,Stream 有四種流類型:

  • Readable - 可讀操做。
  • Writable - 可寫操做。
  • Duplex - 可讀可寫操做.
  • Transform - 操做被寫入數據,而後讀出結果。

全部的 Stream 對象都是 EventEmitter 的實例。經常使用的事件有:

  • data - 當有數據可讀時觸發。
  • end - 沒有更多的數據可讀時觸發。
  • error - 在接收和寫入過程當中發生錯誤時觸發。
  • finish - 全部數據已被寫入到底層系統時觸發。

本教程會爲你們介紹經常使用的流操做。


9.1從流中讀取數據

建立 input.txt 文件,內容以下:

菜鳥教程官網地址:www.runoob.com

建立 main.js 文件, 代碼以下:

var fs = require("fs");
var data = '';
 
// 建立可讀流
var readerStream = fs.createReadStream('input.txt');
 
// 設置編碼爲 utf8。
readerStream.setEncoding('UTF8');
 
// 處理流事件 --> data, end, and error
readerStream.on('data', function(chunk) {
   data += chunk;
});
 
readerStream.on('end',function(){
   console.log(data);
});
 
readerStream.on('error', function(err){
   console.log(err.stack);
});
 
console.log("程序執行完畢");

以上代碼執行結果以下:

程序執行完畢
菜鳥教程官網地址:www.runoob.com

9.2寫入流

建立 main.js 文件, 代碼以下:

var fs = require("fs");
var data = '菜鳥教程官網地址:www.runoob.com';
 
// 建立一個能夠寫入的流,寫入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');
 
// 使用 utf8 編碼寫入數據
writerStream.write(data,'UTF8');
 
// 標記文件末尾
writerStream.end();
 
// 處理流事件 --> data, end, and error
writerStream.on('finish', function() {
    console.log("寫入完成。");
});
 
writerStream.on('error', function(err){
   console.log(err.stack);
});
 
console.log("程序執行完畢");

以上程序會將 data 變量的數據寫入到 output.txt 文件中。代碼執行結果以下:

$ node main.js 
程序執行完畢
寫入完成。

查看 output.txt 文件的內容:

$ cat output.txt 
菜鳥教程官網地址:www.runoob.com

9.3管道流

管道提供了一個輸出流到輸入流的機制。一般咱們用於從一個流中獲取數據並將數據傳遞到另一個流中。

 

如上面的圖片所示,咱們把文件比做裝水的桶,而水就是文件裏的內容,咱們用一根管子(pipe)鏈接兩個桶使得水從一個桶流入另外一個桶,這樣就慢慢的實現了大文件的複製過程。

如下實例咱們經過讀取一個文件內容並將內容寫入到另一個文件中。

設置 input.txt 文件內容以下:

菜鳥教程官網地址:www.runoob.com
管道流操做實例

建立 main.js 文件, 代碼以下:

var fs = require("fs");
 
// 建立一個可讀流
var readerStream = fs.createReadStream('input.txt');
 
// 建立一個可寫流
var writerStream = fs.createWriteStream('output.txt');
 
// 管道讀寫操做
// 讀取 input.txt 文件內容,並將內容寫入到 output.txt 文件中
readerStream.pipe(writerStream);
 
console.log("程序執行完畢");

代碼執行結果以下:

$ node main.js 
程序執行完畢

查看 output.txt 文件的內容:

$ cat output.txt 
菜鳥教程官網地址:www.runoob.com
管道流操做實例

9.4鏈式流

鏈式是經過鏈接輸出流到另一個流並建立多個流操做鏈的機制。鏈式流通常用於管道操做。

接下來咱們就是用管道和鏈式來壓縮和解壓文件。

建立 compress.js 文件, 代碼以下:

var fs = require("fs");
var zlib = require('zlib');
 
// 壓縮 input.txt 文件爲 input.txt.gz
fs.createReadStream('input.txt')
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream('input.txt.gz'));
  
console.log("文件壓縮完成。");

代碼執行結果以下:

$ node compress.js 
文件壓縮完成。

執行完以上操做後,咱們能夠看到當前目錄下生成了 input.txt 的壓縮文件 input.txt.gz。

接下來,讓咱們來解壓該文件,建立 decompress.js 文件,代碼以下:

var fs = require("fs");
var zlib = require('zlib');
 
// 解壓 input.txt.gz 文件爲 input.txt
fs.createReadStream('input.txt.gz')
  .pipe(zlib.createGunzip())
  .pipe(fs.createWriteStream('input.txt'));
  
console.log("文件解壓完成。");

代碼執行結果以下:

$ node decompress.js 
文件解壓完成。

 

10、Node.js模塊系統

爲了讓Node.js的文件能夠相互調用,Node.js提供了一個簡單的模塊系統。

模塊是Node.js 應用程序的基本組成部分,文件和模塊是一一對應的。換言之,一個 Node.js 文件就是一個模塊,這個文件多是JavaScript 代碼、JSON 或者編譯過的C/C++ 擴展。

10.1建立模塊

在 Node.js 中,建立一個模塊很是簡單,以下咱們建立一個 main.js 文件,代碼以下:

var hello = require('./hello');
hello.world();

以上實例中,代碼 require('./hello') 引入了當前目錄下的 hello.js 文件(./ 爲當前目錄,node.js 默認後綴爲 js)。

Node.js 提供了 exports 和 require 兩個對象,其中 exports 是模塊公開的接口,require 用於從外部獲取一個模塊的接口,即所獲取模塊的 exports 對象。

接下來咱們就來建立 hello.js 文件,代碼以下:

exports.world = function() {
  console.log('Hello World');
}

在以上示例中,hello.js 經過 exports 對象把 world 做爲模塊的訪問接口,在 main.js 中經過 require('./hello') 加載這個模塊,而後就能夠直接訪 問 hello.js 中 exports 對象的成員函數了。

有時候咱們只是想把一個對象封裝到模塊中,格式以下:

module.exports = function() {
  // ...
}

例如:

//hello.js 
function Hello() { 
    var name; 
    this.setName = function(thyName) { 
        name = thyName; 
    }; 
    this.sayHello = function() { 
        console.log('Hello ' + name); 
    }; 
}; 
module.exports = Hello;

這樣就能夠直接得到這個對象了:

//main.js 
var Hello = require('./hello'); 
hello = new Hello(); 
hello.setName('BYVoid'); 
hello.sayHello(); 

模塊接口的惟一變化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用該模塊時,其接口對象就是要輸出的 Hello 對象自己,而不是原先的 exports。


10.2服務端的模塊放在哪裏

也許你已經注意到,咱們已經在代碼中使用了模塊了。像這樣:

var http = require("http");
 
...
 
http.createServer(...);

Node.js 中自帶了一個叫作 http 的模塊,咱們在咱們的代碼中請求它並把返回值賦給一個本地變量。

這把咱們的本地變量變成了一個擁有全部 http 模塊所提供的公共方法的對象。

Node.js 的 require 方法中的文件查找策略以下:

因爲 Node.js 中存在 4 類模塊(原生模塊和3種文件模塊),儘管 require 方法極其簡單,可是內部的加載倒是十分複雜的,其加載優先級也各自不一樣。以下圖所示:

 

從文件模塊緩存中加載

儘管原生模塊與文件模塊的優先級不一樣,可是都會優先從文件模塊的緩存中加載已經存在的模塊。

從原生模塊加載

原生模塊的優先級僅次於文件模塊緩存的優先級。require 方法在解析文件名以後,優先檢查模塊是否在原生模塊列表中。以http模塊爲例,儘管在目錄下存在一個 http/http.js/http.node/http.json 文件,require("http") 都不會從這些文件中加載,而是從原生模塊中加載。

原生模塊也有一個緩存區,一樣也是優先從緩存區加載。若是緩存區沒有被加載過,則調用原生模塊的加載方式進行加載和執行。

從文件加載

當文件模塊緩存中不存在,並且不是原生模塊的時候,Node.js 會解析 require 方法傳入的參數,並從文件系統中加載實際的文件,加載過程當中的包裝和編譯細節在前一節中已經介紹過,這裏咱們將詳細描述查找文件模塊的過程,其中,也有一些細節值得知曉。

require方法接受如下幾種參數的傳遞:

  • http、fs、path等,原生模塊。
  • ./mod或../mod,相對路徑的文件模塊。
  • /pathtomodule/mod,絕對路徑的文件模塊。
  • mod,非原生模塊的文件模塊。

在路徑 Y 下執行 require(X) 語句執行順序:

1. 若是 X 是內置模塊
   a. 返回內置模塊
   b. 中止執行
2. 若是 X 以 '/' 開頭
   a. 設置 Y 爲文件根路徑
3. 若是 X 以 './' 或 '/' or '../' 開頭
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. 拋出異常 "not found"
 
LOAD_AS_FILE(X)
1. 若是 X 是一個文件, 將 X 做爲 JavaScript 文本載入並中止執行。
2. 若是 X.js 是一個文件, 將 X.js 做爲 JavaScript 文本載入並中止執行。
3. 若是 X.json 是一個文件, 解析 X.json 爲 JavaScript 對象並中止執行。
4. 若是 X.node 是一個文件, 將 X.node 做爲二進制插件載入並中止執行。
 
LOAD_INDEX(X)
1. 若是 X/index.js 是一個文件,  將 X/index.js 做爲 JavaScript 文本載入並中止執行。
2. 若是 X/index.json 是一個文件, 解析 X/index.json 爲 JavaScript 對象並中止執行。
3. 若是 X/index.node 是一個文件,  將 X/index.node 做爲二進制插件載入並中止執行。
 
LOAD_AS_DIRECTORY(X)
1. 若是 X/package.json 是一個文件,
   a. 解析 X/package.json, 並查找 "main" 字段。
   b. let M = X + (json main 字段)
   c. LOAD_AS_FILE(M)
   d. LOAD_INDEX(M)
2. LOAD_INDEX(X)
 
LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)
 
NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS

11、Node.js 函數

在JavaScript中,一個函數能夠做爲另外一個函數的參數。咱們能夠先定義一個函數,而後傳遞,也能夠在傳遞參數的地方直接定義函數。

Node.js中函數的使用與Javascript相似,舉例來講,你能夠這樣作:

function say(word) {
  console.log(word);
}
 
function execute(someFunction, value) {
  someFunction(value);
}
 
execute(say, "Hello");

以上代碼中,咱們把 say 函數做爲execute函數的第一個變量進行了傳遞。這裏傳遞的不是 say 的返回值,而是 say 自己!

這樣一來, say 就變成了execute 中的本地變量 someFunction ,execute能夠經過調用 someFunction() (帶括號的形式)來使用 say 函數。

固然,由於 say 有一個變量, execute 在調用 someFunction 時能夠傳遞這樣一個變量。


11.1匿名函數

咱們能夠把一個函數做爲變量傳遞。可是咱們不必定要繞這個"先定義,再傳遞"的圈子,咱們能夠直接在另外一個函數的括號中定義和傳遞這個函數:

function execute(someFunction, value) {
  someFunction(value);
}
 
execute(function(word){ console.log(word) }, "Hello");

咱們在 execute 接受第一個參數的地方直接定義了咱們準備傳遞給 execute 的函數。

用這種方式,咱們甚至不用給這個函數起名字,這也是爲何它被叫作匿名函數 。


11.2函數傳遞是如何讓HTTP服務器工做的

帶着這些知識,咱們再來看看咱們簡約而不簡單的HTTP服務器:

var http = require("http");
 
http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}).listen(8888);

如今它看上去應該清晰了不少:咱們向 createServer 函數傳遞了一個匿名函數。

用這樣的代碼也能夠達到一樣的目的:

var http = require("http");
 
function onRequest(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}
 
http.createServer(onRequest).listen(8888);

12、Node.js 路由

咱們要爲路由提供請求的 URL 和其餘須要的 GET 及 POST 參數,隨後路由須要根據這些數據來執行相應的代碼。

所以,咱們須要查看 HTTP 請求,從中提取出請求的 URL 以及 GET/POST 參數。這一功能應當屬於路由仍是服務器(甚至做爲一個模塊自身的功能)確實值得探討,但這裏暫定其爲咱們的HTTP服務器的功能。

咱們須要的全部數據都會包含在 request 對象中,該對象做爲 onRequest() 回調函數的第一個參數傳遞。可是爲了解析這些數據,咱們須要額外的 Node.JS 模塊,它們分別是 url 和 querystring 模塊。

                   url.parse(string).query
                                           |
           url.parse(string).pathname      |
                       |                   |
                       |                   |
                     ------ -------------------
http://localhost:8888/start?foo=bar&hello=world
                                ---       -----
                                 |          |
                                 |          |
              querystring.parse(queryString)["foo"]    |
                                            |
                         querystring.parse(queryString)["hello"]

固然咱們也能夠用 querystring 模塊來解析 POST 請求體中的參數,稍後會有演示。

如今咱們來給 onRequest() 函數加上一些邏輯,用來找出瀏覽器請求的 URL 路徑:

server.js 文件代碼:

var http = require("http");

var url = require("url");

function start() { function onRequest(request, response) {

var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received.");

 response.writeHead(200, {"Content-Type": "text/plain"});

response.write("Hello World");

response.end(); }

http.createServer(onRequest).listen(8888);

console.log("Server has started."); }

 exports.start = start;

好了,咱們的應用如今能夠經過請求的 URL 路徑來區別不一樣請求了--這使咱們得以使用路由(還未完成)來將請求以 URL 路徑爲基準映射處處理程序上。

在咱們所要構建的應用中,這意味着來自 /start 和 /upload 的請求可使用不一樣的代碼來處理。稍後咱們將看到這些內容是如何整合到一塊兒的。

如今咱們能夠來編寫路由了,創建一個名爲 router.js 的文件,添加如下內容:

router.js 文件代碼:

function route(pathname) { console.log("About to route a request for " + pathname); } exports.route = route;

如你所見,這段代碼什麼也沒幹,不過對於如今來講這是應該的。在添加更多的邏輯之前,咱們先來看看如何把路由和服務器整合起來。

咱們的服務器應當知道路由的存在並加以有效利用。咱們固然能夠經過硬編碼的方式將這一依賴項綁定到服務器上,可是其它語言的編程經驗告訴咱們這會是一件很是痛苦的事,所以咱們將使用依賴注入的方式較鬆散地添加路由模塊。

首先,咱們來擴展一下服務器的 start() 函數,以便將路由函數做爲參數傳遞過去,server.js 文件代碼以下

server.js 文件代碼:

var http = require("http"); var url = require("url"); function start(route) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(pathname); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

同時,咱們會相應擴展 index.js,使得路由函數能夠被注入到服務器中:

index.js 文件代碼:

var server = require("./server"); var router = require("./router"); server.start(router.route);

在這裏,咱們傳遞的函數依舊什麼也沒作。

若是如今啓動應用(node index.js,始終記得這個命令行),隨後請求一個URL,你將會看到應用輸出相應的信息,這代表咱們的HTTP服務器已經在使用路由模塊了,並會將請求的路徑傳遞給路由:

$ node index.js
Server has started.

以上輸出已經去掉了比較煩人的 /favicon.ico 請求相關的部分。

瀏覽器訪問 http://127.0.0.1:8888/,輸出結果以下:

 

十3、Node.js 全局對象

JavaScript 中有一個特殊的對象,稱爲全局對象(Global Object),它及其全部屬性均可以在程序的任何地方訪問,即全局變量。

在瀏覽器 JavaScript 中,一般 window 是全局對象, 而 Node.js 中的全局對象是 global,全部全局變量(除了 global 自己之外)都是 global 對象的屬性。

在 Node.js 咱們能夠直接訪問到 global 的屬性,而不須要在應用中包含它。


全局對象與全局變量

global 最根本的做用是做爲全局變量的宿主。按照 ECMAScript 的定義,知足如下條 件的變量是全局變量:

  • 在最外層定義的變量;
  • 全局對象的屬性;
  • 隱式定義的變量(未定義直接賦值的變量)。

當你定義一個全局變量時,這個變量同時也會成爲全局對象的屬性,反之亦然。須要注 意的是,在 Node.js 中你不可能在最外層定義變量,由於全部用戶代碼都是屬於當前模塊的, 而模塊自己不是最外層上下文。

注意: 永遠使用 var 定義變量以免引入全局變量,由於全局變量會污染 命名空間,提升代碼的耦合風險。


__filename

__filename 表示當前正在執行的腳本的文件名。它將輸出文件所在位置的絕對路徑,且和命令行參數所指定的文件名不必定相同。 若是在模塊中,返回的值是模塊文件的路徑。

實例

建立文件 main.js ,代碼以下所示:

// 輸出全局變量 __filename 的值
console.log( __filename );

執行 main.js 文件,代碼以下所示:

$ node main.js
/web/com/runoob/nodejs/main.js

__dirname

__dirname 表示當前執行腳本所在的目錄。

實例

建立文件 main.js ,代碼以下所示:

// 輸出全局變量 __dirname 的值
console.log( __dirname );

執行 main.js 文件,代碼以下所示:

$ node main.js
/web/com/runoob/nodejs

setTimeout(cb, ms)

setTimeout(cb, ms) 全局函數在指定的毫秒(ms)數後執行指定函數(cb)。:setTimeout() 只執行一次指定函數。

返回一個表明定時器的句柄值。

實例

建立文件 main.js ,代碼以下所示:

function printHello(){
   console.log( "Hello, World!");
}
// 兩秒後執行以上函數
setTimeout(printHello, 2000);

執行 main.js 文件,代碼以下所示:

$ node main.js
Hello, World!

clearTimeout(t)

clearTimeout( t ) 全局函數用於中止一個以前經過 setTimeout() 建立的定時器。 參數 t 是經過 setTimeout() 函數建立的定時器。

實例

建立文件 main.js ,代碼以下所示:

function printHello(){
   console.log( "Hello, World!");
}
// 兩秒後執行以上函數
var t = setTimeout(printHello, 2000);
 
// 清除定時器
clearTimeout(t);

執行 main.js 文件,代碼以下所示:

$ node main.js

setInterval(cb, ms)

setInterval(cb, ms) 全局函數在指定的毫秒(ms)數後執行指定函數(cb)。

返回一個表明定時器的句柄值。可使用 clearInterval(t) 函數來清除定時器。

setInterval() 方法會不停地調用函數,直到 clearInterval() 被調用或窗口被關閉。

實例

建立文件 main.js ,代碼以下所示:

function printHello(){
   console.log( "Hello, World!");
}
// 兩秒後執行以上函數
setInterval(printHello, 2000);

執行 main.js 文件,代碼以下所示:

$ node main.js

Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! ……

以上程序每隔兩秒就會輸出一次"Hello, World!",且會永久執行下去,直到你按下 ctrl + c 按鈕。


console

console 用於提供控制檯標準輸出,它是由 Internet Explorer 的 JScript 引擎提供的調試工具,後來逐漸成爲瀏覽器的實施標準。

Node.js 沿用了這個標準,提供與習慣行爲一致的 console 對象,用於向標準輸出流(stdout)或標準錯誤流(stderr)輸出字符。

console 方法

如下爲 console 對象的方法:

序號

方法 & 描述

1

console.log([data][, ...])
向標準輸出流打印字符並以換行符結束。該方法接收若干 個參數,若是隻有一個參數,則輸出這個參數的字符串形式。若是有多個參數,則 以相似於C 語言 printf() 命令的格式輸出。

2

console.info([data][, ...])
該命令的做用是返回信息性消息,這個命令與console.log差異並不大,除了在chrome中只會輸出文字外,其他的會顯示一個藍色的驚歎號。

3

console.error([data][, ...])
輸出錯誤消息的。控制檯在出現錯誤時會顯示是紅色的叉子。

4

console.warn([data][, ...])
輸出警告消息。控制檯出現有黃色的驚歎號。

5

console.dir(obj[, options])
用來對一個對象進行檢查(inspect),並以易於閱讀和打印的格式顯示。

6

console.time(label)
輸出時間,表示計時開始。

7

console.timeEnd(label)
結束時間,表示計時結束。

8

console.trace(message[, ...])
當前執行的代碼在堆棧中的調用路徑,這個測試函數運行頗有幫助,只要給想測試的函數裏面加入 console.trace 就好了。

9

console.assert(value[, message][, ...])
用於判斷某個表達式或變量是否爲真,接收兩個參數,第一個參數是表達式,第二個參數是字符串。只有當第一個參數爲false,纔會輸出第二個參數,不然不會有任何結果。

console.log():向標準輸出流打印字符並以換行符結束。

console.log 接收若干 個參數,若是隻有一個參數,則輸出這個參數的字符串形式。若是有多個參數,則 以相似於C 語言 printf() 命令的格式輸出。

第一個參數是一個字符串,若是沒有 參數,只打印一個換行。

console.log('Hello world'); 
console.log('byvoid%diovyb'); 
console.log('byvoid%diovyb', 1991); 

運行結果爲:

Hello world 
byvoid%diovyb 
byvoid1991iovyb 
  • console.error():與console.log() 用法相同,只是向標準錯誤流輸出。
  • console.trace():向標準錯誤流輸出當前的調用棧。
console.trace();

運行結果爲:

Trace: 
at Object.<anonymous> (/home/byvoid/consoletrace.js:1:71) 
at Module._compile (module.js:441:26) 
at Object..js (module.js:459:10) 
at Module.load (module.js:348:31) 
at Function._load (module.js:308:12) 
at Array.0 (module.js:479:10) 
at EventEmitter._tickCallback (node.js:192:40)

實例

建立文件 main.js ,代碼以下所示:

console.info("程序開始執行:");
 
var counter = 10;
console.log("計數: %d", counter);
 
console.time("獲取數據");
//
// 執行一些代碼
// 
console.timeEnd('獲取數據');
 
console.info("程序執行完畢。")

執行 main.js 文件,代碼以下所示:

$ node main.js
程序開始執行:
計數: 10
獲取數據: 0ms
程序執行完畢

process

process 是一個全局變量,即 global 對象的屬性。

它用於描述當前Node.js 進程狀態的對象,提供了一個與操做系統的簡單接口。一般在你寫本地命令行程序的時候,少不了要 和它打交道。下面將會介紹 process 對象的一些最經常使用的成員方法。

序號

事件 & 描述

1

exit
當進程準備退出時觸發。

2

beforeExit
當 node 清空事件循環,而且沒有其餘安排時觸發這個事件。一般來講,當沒有進程安排時 node 退出,可是 'beforeExit' 的監聽器能夠異步調用,這樣 node 就會繼續執行。

3

uncaughtException
當一個異常冒泡回到事件循環,觸發這個事件。若是給異常添加了監視器,默認的操做(打印堆棧跟蹤信息並退出)就不會發生。

4

Signal 事件
當進程接收到信號時就觸發。信號列表詳見標準的 POSIX 信號名,如 SIGINT、SIGUSR1 等。

實例

建立文件 main.js ,代碼以下所示:

process.on('exit', function(code) {
 
  // 如下代碼永遠不會執行
  setTimeout(function() {
    console.log("該代碼不會執行");
  }, 0);
  
  console.log('退出碼爲:', code);
});
console.log("程序執行結束");

執行 main.js 文件,代碼以下所示:

$ node main.js
程序執行結束
退出碼爲: 0

退出狀態碼

退出狀態碼以下所示:

狀態碼

名稱 & 描述

1

Uncaught Fatal Exception
有未捕獲異常,而且沒有被域或 uncaughtException 處理函數處理。

2

Unused
保留

3

Internal JavaScript Parse Error
JavaScript的源碼啓動 Node 進程時引發解析錯誤。很是罕見,僅會在開發 Node 時纔會有。

4

Internal JavaScript Evaluation Failure
JavaScript 的源碼啓動 Node 進程,評估時返回函數失敗。很是罕見,僅會在開發 Node 時纔會有。

5

Fatal Error
V8 裏致命的不可恢復的錯誤。一般會打印到 stderr ,內容爲: FATAL ERROR

6

Non-function Internal Exception Handler
未捕獲異常,內部異常處理函數不知爲什麼設置爲on-function,而且不能被調用。

7

Internal Exception Handler Run-Time Failure
未捕獲的異常, 而且異常處理函數處理時本身拋出了異常。例如,若是 process.on('uncaughtException') 或 domain.on('error') 拋出了異常。

8

Unused
保留

9

Invalid Argument
多是給了未知的參數,或者給的參數沒有值。

10

Internal JavaScript Run-Time Failure
JavaScript的源碼啓動 Node 進程時拋出錯誤,很是罕見,僅會在開發 Node 時纔會有。

12

Invalid Debug Argument 
設置了參數--debug 和/或 --debug-brk,可是選擇了錯誤端口。

128

Signal Exits
若是 Node 接收到致命信號,好比SIGKILL 或 SIGHUP,那麼退出代碼就是128 加信號代碼。這是標準的 Unix 作法,退出信號代碼放在高位。

Process 屬性

Process 提供了不少有用的屬性,便於咱們更好的控制系統的交互:

序號.

屬性 & 描述

1

stdout
標準輸出流。

2

stderr
標準錯誤流。

3

stdin
標準輸入流。

4

argv
argv 屬性返回一個數組,由命令行執行腳本時的各個參數組成。它的第一個成員老是node,第二個成員是腳本文件名,其他成員是腳本文件的參數。

5

execPath
返回執行當前腳本的 Node 二進制文件的絕對路徑。

6

execArgv
返回一個數組,成員是命令行下執行腳本時,在Node可執行文件與腳本文件之間的命令行參數。

7

env
返回一個對象,成員爲當前 shell 的環境變量

8

exitCode
進程退出時的代碼,若是進程優經過 process.exit() 退出,不須要指定退出碼。

9

version
Node 的版本,好比v0.10.18。

10

versions
一個屬性,包含了 node 的版本和依賴.

11

config
一個包含用來編譯當前 node 執行文件的 javascript 配置選項的對象。它與運行 ./configure 腳本生成的 "config.gypi" 文件相同。

12

pid
當前進程的進程號。

13

title
進程名,默認值爲"node",能夠自定義該值。

14

arch
當前 CPU 的架構:'arm'、'ia32' 或者 'x64'。

15

platform
運行程序所在的平臺系統 'darwin', 'freebsd', 'linux', 'sunos' 或 'win32'

16

mainModule
require.main 的備選方法。不一樣點,若是主模塊在運行時改變,require.main可能會繼續返回老的模塊。能夠認爲,這二者引用了同一個模塊。

實例

建立文件 main.js ,代碼以下所示:

// 輸出到終端
process.stdout.write("Hello World!" + "\n");
 
// 經過參數讀取
process.argv.forEach(function(val, index, array) {
   console.log(index + ': ' + val);
});
 
// 獲取執行路徑
console.log(process.execPath);
 
 
// 平臺信息
console.log(process.platform);

執行 main.js 文件,代碼以下所示:

$ node main.js
Hello World!
0: node
1: /web/www/node/main.js
/usr/local/node/0.10.36/bin/node
darwin

方法參考手冊

Process 提供了不少有用的方法,便於咱們更好的控制系統的交互:

序號

方法 & 描述

1

abort()
這將致使 node 觸發 abort 事件。會讓 node 退出並生成一個核心文件。

2

chdir(directory)
改變當前工做進程的目錄,若是操做失敗拋出異常。

3

cwd()
返回當前進程的工做目錄

4

exit([code])
使用指定的 code 結束進程。若是忽略,將會使用 code 0。

5

getgid()
獲取進程的羣組標識(參見 getgid(2))。獲取到得時羣組的數字 id,而不是名字。
注意:這個函數僅在 POSIX 平臺上可用(例如,非Windows 和 Android)。

6

setgid(id)
設置進程的羣組標識(參見 setgid(2))。能夠接收數字 ID 或者羣組名。若是指定了羣組名,會阻塞等待解析爲數字 ID 。
注意:這個函數僅在 POSIX 平臺上可用(例如,非Windows 和 Android)。

7

getuid()
獲取進程的用戶標識(參見 getuid(2))。這是數字的用戶 id,不是用戶名。
注意:這個函數僅在 POSIX 平臺上可用(例如,非Windows 和 Android)。

8

setuid(id)
設置進程的用戶標識(參見setuid(2))。接收數字 ID或字符串名字。果指定了羣組名,會阻塞等待解析爲數字 ID 。
注意:這個函數僅在 POSIX 平臺上可用(例如,非Windows 和 Android)。

9

getgroups()
返回進程的羣組 iD 數組。POSIX 系統沒有保證必定有,可是 node.js 保證有。
注意:這個函數僅在 POSIX 平臺上可用(例如,非Windows 和 Android)。

10

setgroups(groups)
設置進程的羣組 ID。這是受權操做,全部你須要有 root 權限,或者有 CAP_SETGID 能力。
注意:這個函數僅在 POSIX 平臺上可用(例如,非Windows 和 Android)。

11

initgroups(user, extra_group)
讀取 /etc/group ,並初始化羣組訪問列表,使用成員所在的全部羣組。這是受權操做,全部你須要有 root 權限,或者有 CAP_SETGID 能力。
注意:這個函數僅在 POSIX 平臺上可用(例如,非Windows 和 Android)。

12

kill(pid[, signal])
發送信號給進程. pid 是進程id,而且 signal 是發送的信號的字符串描述。信號名是字符串,好比 'SIGINT' 或 'SIGHUP'。若是忽略,信號會是 'SIGTERM'。

13

memoryUsage()
返回一個對象,描述了 Node 進程所用的內存情況,單位爲字節。

14

nextTick(callback)
一旦當前事件循環結束,調用回到函數。

15

umask([mask])
設置或讀取進程文件的掩碼。子進程從父進程繼承掩碼。若是mask 參數有效,返回舊的掩碼。不然,返回當前掩碼。

16

uptime()
返回 Node 已經運行的秒數。

17

hrtime()
返回當前進程的高分辨時間,形式爲 [seconds, nanoseconds]數組。它是相對於過去的任意事件。該值與日期無關,所以不受時鐘漂移的影響。主要用途是能夠經過精確的時間間隔,來衡量程序的性能。
你能夠將以前的結果傳遞給當前的 process.hrtime() ,會返回二者間的時間差,用來基準和測量時間間隔。

實例

建立文件 main.js ,代碼以下所示:

// 輸出當前目錄
console.log('當前目錄: ' + process.cwd());
 
// 輸出當前版本
console.log('當前版本: ' + process.version);
 
// 輸出內存使用狀況
console.log(process.memoryUsage());

執行 main.js 文件,代碼以下所示:

$ node main.js
當前目錄: /web/com/runoob/nodejs
當前版本: v0.10.36
{ rss: 12541952, heapTotal: 4083456, heapUsed: 2157056 }

十4、Node.js 經常使用工具

util 是一個Node.js 核心模塊,提供經常使用函數的集合,用於彌補核心JavaScript 的功能 過於精簡的不足。


util.inherits

util.inherits(constructor, superConstructor)是一個實現對象間原型繼承 的函數。

JavaScript 的面向對象特性是基於原型的,與常見的基於類的不一樣。JavaScript 沒有 提供對象繼承的語言級別特性,而是經過原型複製來實現的。

在這裏咱們只介紹util.inherits 的用法,示例以下:

var util = require('util'); 
function Base() { 
    this.name = 'base'; 
    this.base = 1991; 
    this.sayHello = function() { 
    console.log('Hello ' + this.name); 
    }; 
} 
Base.prototype.showName = function() { 
    console.log(this.name);
}; 
function Sub() { 
    this.name = 'sub'; 
} 
util.inherits(Sub, Base); 
var objBase = new Base(); 
objBase.showName(); 
objBase.sayHello(); 
console.log(objBase); 
var objSub = new Sub(); 
objSub.showName(); 
//objSub.sayHello(); 
console.log(objSub); 

咱們定義了一個基礎對象Base 和一個繼承自Base 的Sub,Base 有三個在構造函數 內定義的屬性和一個原型中定義的函數,經過util.inherits 實現繼承。運行結果以下:

base 
Hello base 
{ name: 'base', base: 1991, sayHello: [Function] } 
sub 
{ name: 'sub' }

注意:Sub 僅僅繼承了Base 在原型中定義的函數,而構造函數內部創造的 base 屬 性和 sayHello 函數都沒有被 Sub 繼承。

同時,在原型中定義的屬性不會被console.log 做 爲對象的屬性輸出。若是咱們去掉 objSub.sayHello(); 這行的註釋,將會看到:

node.js:201 
throw e; // process.nextTick error, or 'error' event on first tick 
^ 
TypeError: Object #&lt;Sub&gt; has no method 'sayHello' 
at Object.&lt;anonymous&gt; (/home/byvoid/utilinherits.js:29:8) 
at Module._compile (module.js:441:26) 
at Object..js (module.js:459:10) 
at Module.load (module.js:348:31) 
at Function._load (module.js:308:12) 
at Array.0 (module.js:479:10) 
at EventEmitter._tickCallback (node.js:192:40) 

util.inspect

util.inspect(object,[showHidden],[depth],[colors])是一個將任意對象轉換 爲字符串的方法,一般用於調試和錯誤輸出。它至少接受一個參數 object,即要轉換的對象。

showHidden 是一個可選參數,若是值爲 true,將會輸出更多隱藏信息。

depth 表示最大遞歸的層數,若是對象很複雜,你能夠指定層數以控制輸出信息的多 少。若是不指定depth,默認會遞歸2層,指定爲 null 表示將不限遞歸層數完整遍歷對象。 若是color 值爲 true,輸出格式將會以ANSI 顏色編碼,一般用於在終端顯示更漂亮 的效果。

特別要指出的是,util.inspect 並不會簡單地直接把對象轉換爲字符串,即便該對 象定義了toString 方法也不會調用。

var util = require('util'); 
function Person() { 
    this.name = 'byvoid'; 
    this.toString = function() { 
    return this.name; 
    }; 
} 
var obj = new Person(); 
console.log(util.inspect(obj)); 
console.log(util.inspect(obj, true)); 

運行結果是:

Person { name: 'byvoid', toString: [Function] }
Person {
  name: 'byvoid',
  toString: 
   { [Function]
     [length]: 0,
     [name]: '',
     [arguments]: null,
     [caller]: null,
     [prototype]: { [constructor]: [Circular] } } }

util.isArray(object)

若是給定的參數 "object" 是一個數組返回true,不然返回false。

var util = require('util');
 
util.isArray([])
  // true
util.isArray(new Array)
  // true
util.isArray({})
  // false

util.isRegExp(object)

若是給定的參數 "object" 是一個正則表達式返回true,不然返回false。

var util = require('util');
 
util.isRegExp(/some regexp/)
  // true
util.isRegExp(new RegExp('another regexp'))
  // true
util.isRegExp({})
  // false

util.isDate(object)

若是給定的參數 "object" 是一個日期返回true,不然返回false。

var util = require('util');
 
util.isDate(new Date())
  // true
util.isDate(Date())
  // false (without 'new' returns a String)
util.isDate({})
  // false

util.isError(object)

若是給定的參數 "object" 是一個錯誤對象返回true,不然返回false。

var util = require('util');
 
util.isError(new Error())
  // true
util.isError(new TypeError())
  // true
util.isError({ name: 'Error', message: 'an error occurred' })
  // false

十5、Node.js 文件系統

Node.js 提供一組相似 UNIX(POSIX)標準的文件操做API。 Node 導入文件系統模塊(fs)語法以下所示:

var fs = require("fs")

15.1異步和同步

Node.js 文件系統(fs 模塊)模塊中的方法均有異步和同步版本,例如讀取文件內容的函數有異步的 fs.readFile() 和同步的 fs.readFileSync()。

異步的方法函數最後一個參數爲回調函數,回調函數的第一個參數包含了錯誤信息(error)。

建議你們使用異步方法,比起同步,異步方法性能更高,速度更快,並且沒有阻塞。

實例

建立 input.txt 文件,內容以下:

菜鳥教程官網地址:www.runoob.com
文件讀取實例

建立 file.js 文件, 代碼以下:

var fs = require("fs");
 
// 異步讀取
fs.readFile('input.txt', function (err, data) {
   if (err) {
       return console.error(err);
   }
   console.log("異步讀取: " + data.toString());
});
 
// 同步讀取
var data = fs.readFileSync('input.txt');
console.log("同步讀取: " + data.toString());
 
console.log("程序執行完畢。");

以上代碼執行結果以下:

$ node file.js 
同步讀取: 菜鳥教程官網地址:www.runoob.com
文件讀取實例
 
程序執行完畢。
異步讀取: 菜鳥教程官網地址:www.runoob.com
文件讀取實例

接下來,讓咱們來具體瞭解下 Node.js 文件系統的方法。


15.2打開文件

語法

如下爲在異步模式下打開文件的語法格式:

fs.open(path, flags[, mode], callback)

參數

參數使用說明以下:

  • path - 文件的路徑。
  • flags - 文件打開的行爲。具體值詳見下文。
  • mode - 設置文件模式(權限),文件建立默認權限爲 0666(可讀,可寫)。
  • callback - 回調函數,帶有兩個參數如:callback(err, fd)。

flags 參數能夠是如下值:

Flag

描述

r

以讀取模式打開文件。若是文件不存在拋出異常。

r+

以讀寫模式打開文件。若是文件不存在拋出異常。

rs

以同步的方式讀取文件。

rs+

以同步的方式讀取和寫入文件。

w

以寫入模式打開文件,若是文件不存在則建立。

wx

相似 'w',可是若是文件路徑存在,則文件寫入失敗。

w+

以讀寫模式打開文件,若是文件不存在則建立。

wx+

相似 'w+', 可是若是文件路徑存在,則文件讀寫失敗。

a

以追加模式打開文件,若是文件不存在則建立。

ax

相似 'a', 可是若是文件路徑存在,則文件追加失敗。

a+

以讀取追加模式打開文件,若是文件不存在則建立。

ax+

相似 'a+', 可是若是文件路徑存在,則文件讀取追加失敗。

實例

接下來咱們建立 file.js 文件,並打開 input.txt 文件進行讀寫,代碼以下所示:

var fs = require("fs");
 
// 異步打開文件
console.log("準備打開文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
  console.log("文件打開成功!");     
});

以上代碼執行結果以下:

$ node file.js 
準備打開文件!
文件打開成功!

15.3獲取文件信息

語法

如下爲經過異步模式獲取文件信息的語法格式:

fs.stat(path, callback)

參數

參數使用說明以下:

  • path - 文件路徑。
  • callback - 回調函數,帶有兩個參數如:(err, stats), stats 是 fs.Stats 對象。

fs.stat(path)執行後,會將stats類的實例返回給其回調函數。能夠經過stats類中的提供方法判斷文件的相關屬性。例如判斷是否爲文件:

var fs = require('fs');
 
fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) {
    console.log(stats.isFile());         //true
})

stats類中的方法有:

方法

描述

stats.isFile()

若是是文件返回 true,不然返回 false。

stats.isDirectory()

若是是目錄返回 true,不然返回 false。

stats.isBlockDevice()

若是是塊設備返回 true,不然返回 false。

stats.isCharacterDevice()

若是是字符設備返回 true,不然返回 false。

stats.isSymbolicLink()

若是是軟連接返回 true,不然返回 false。

stats.isFIFO()

若是是FIFO,返回true,不然返回 false。FIFO是UNIX中的一種特殊類型的命令管道。

stats.isSocket()

若是是 Socket 返回 true,不然返回 false。

實例

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
 
console.log("準備打開文件!");
fs.stat('input.txt', function (err, stats) {
   if (err) {
       return console.error(err);
   }
   console.log(stats);
   console.log("讀取文件信息成功!");
   
   // 檢測文件類型
   console.log("是否爲文件(isFile) ? " + stats.isFile());
   console.log("是否爲目錄(isDirectory) ? " + stats.isDirectory());    
});

以上代碼執行結果以下:

$ node file.js 
準備打開文件!
{ dev: 16777220,
  mode: 33188,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 40333161,
  size: 61,
  blocks: 8,
  atime: Mon Sep 07 2015 17:43:55 GMT+0800 (CST),
  mtime: Mon Sep 07 2015 17:22:35 GMT+0800 (CST),
  ctime: Mon Sep 07 2015 17:22:35 GMT+0800 (CST) }
讀取文件信息成功!
是否爲文件(isFile) ? true
是否爲目錄(isDirectory) ? false

15.4寫入文件

語法

如下爲異步模式下寫入文件的語法格式:

fs.writeFile(file, data[, options], callback)

writeFile 直接打開文件默認是 w 模式,因此若是文件存在,該方法寫入的內容會覆蓋舊的文件內容。

參數

參數使用說明以下:

  • file - 文件名或文件描述符。
  • data - 要寫入文件的數據,能夠是 String(字符串) 或 Buffer(流) 對象。
  • options - 該參數是一個對象,包含 {encoding, mode, flag}。默認編碼爲 utf8, 模式爲 0666 , flag 爲 'w'
  • callback - 回調函數,回調函數只包含錯誤信息參數(err),在寫入失敗時返回。

實例

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
 
console.log("準備寫入文件");
fs.writeFile('input.txt', '我是通 過fs.writeFile 寫入文件的內容',  function(err) {
   if (err) {
       return console.error(err);
   }
   console.log("數據寫入成功!");
   console.log("--------我是分割線-------------")
   console.log("讀取寫入的數據!");
   fs.readFile('input.txt', function (err, data) {
      if (err) {
         return console.error(err);
      }
      console.log("異步讀取文件數據: " + data.toString());
   });
});

以上代碼執行結果以下:

$ node file.js 
準備寫入文件
數據寫入成功!
--------我是分割線-------------
讀取寫入的數據!
異步讀取文件數據: 我是通 過fs.writeFile 寫入文件的內容

15.5讀取文件

語法

如下爲異步模式下讀取文件的語法格式:

fs.read(fd, buffer, offset, length, position, callback)

該方法使用了文件描述符來讀取文件。

參數

參數使用說明以下:

  • fd - 經過 fs.open() 方法返回的文件描述符。
  • buffer - 數據寫入的緩衝區。
  • offset - 緩衝區寫入的寫入偏移量。
  • length - 要從文件中讀取的字節數。
  • position - 文件讀取的起始位置,若是 position 的值爲 null,則會從當前文件指針的位置讀取。
  • callback - 回調函數,有三個參數err, bytesRead, buffer,err 爲錯誤信息, bytesRead 表示讀取的字節數,buffer 爲緩衝區對象。

實例

input.txt 文件內容爲:

菜鳥教程官網地址:www.runoob.com

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
var buf = new Buffer(1024);
 
console.log("準備打開已存在的文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
   console.log("文件打開成功!");
   console.log("準備讀取文件:");
   fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
      if (err){
         console.log(err);
      }
      console.log(bytes + "  字節被讀取");
      
      // 僅輸出讀取的字節
      if(bytes > 0){
         console.log(buf.slice(0, bytes).toString());
      }
   });
});

以上代碼執行結果以下:

$ node file.js 
準備打開已存在的文件!
文件打開成功!
準備讀取文件:
42  字節被讀取
菜鳥教程官網地址:www.runoob.com

15.6關閉文件

語法

如下爲異步模式下關閉文件的語法格式:

fs.close(fd, callback)

該方法使用了文件描述符來讀取文件。

參數

參數使用說明以下:

  • fd - 經過 fs.open() 方法返回的文件描述符。
  • callback - 回調函數,沒有參數。

實例

input.txt 文件內容爲:

菜鳥教程官網地址:www.runoob.com

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
var buf = new Buffer(1024);
 
console.log("準備打開文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
   console.log("文件打開成功!");
   console.log("準備讀取文件!");
   fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
      if (err){
         console.log(err);
      }
 
      // 僅輸出讀取的字節
      if(bytes > 0){
         console.log(buf.slice(0, bytes).toString());
      }
 
      // 關閉文件
      fs.close(fd, function(err){
         if (err){
            console.log(err);
         } 
         console.log("文件關閉成功");
      });
   });
});

以上代碼執行結果以下:

$ node file.js 
準備打開文件!
文件打開成功!
準備讀取文件!
菜鳥教程官網地址:www.runoob.com
文件關閉成功

15.7截取文件

語法

如下爲異步模式下截取文件的語法格式:

fs.ftruncate(fd, len, callback)

該方法使用了文件描述符來讀取文件。

參數

參數使用說明以下:

  • fd - 經過 fs.open() 方法返回的文件描述符。
  • len - 文件內容截取的長度。
  • callback - 回調函數,沒有參數。

實例

input.txt 文件內容爲:

site:www.runoob.com

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
var buf = new Buffer(1024);
 
console.log("準備打開文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
   console.log("文件打開成功!");
   console.log("截取了10字節後的文件內容。");
   
   // 截取文件
   fs.ftruncate(fd, 10, function(err){
      if (err){
         console.log(err);
      } 
      console.log("文件截取成功。");
      console.log("讀取相同的文件"); 
      fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
         if (err){
            console.log(err);
         }
 
         // 僅輸出讀取的字節
         if(bytes > 0){
            console.log(buf.slice(0, bytes).toString());
         }
 
         // 關閉文件
         fs.close(fd, function(err){
            if (err){
               console.log(err);
            } 
            console.log("文件關閉成功!");
         });
      });
   });
});

以上代碼執行結果以下:

$ node file.js 
準備打開文件!
文件打開成功!
截取了10字節後的文件內容。
文件截取成功。
讀取相同的文件
site:www.r
文件關閉成功

15.8刪除文件

語法

如下爲刪除文件的語法格式:

fs.unlink(path, callback)

參數

參數使用說明以下:

  • path - 文件路徑。
  • callback - 回調函數,沒有參數。

實例

input.txt 文件內容爲:

site:www.runoob.com

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
 
console.log("準備刪除文件!");
fs.unlink('input.txt', function(err) {
   if (err) {
       return console.error(err);
   }
   console.log("文件刪除成功!");
});

以上代碼執行結果以下:

$ node file.js 
準備刪除文件!
文件刪除成功!

再去查看 input.txt 文件,發現已經不存在了。


15.9建立目錄

語法

如下爲建立目錄的語法格式:

fs.mkdir(path[, mode], callback)

參數

參數使用說明以下:

  • path - 文件路徑。
  • mode - 設置目錄權限,默認爲 0777。
  • callback - 回調函數,沒有參數。

實例

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
 
console.log("建立目錄 /tmp/test/");
fs.mkdir("/tmp/test/",function(err){
   if (err) {
       return console.error(err);
   }
   console.log("目錄建立成功。");
});

以上代碼執行結果以下:

$ node file.js 
建立目錄 /tmp/test/
目錄建立成功。

15.10讀取目錄

語法

如下爲讀取目錄的語法格式:

fs.readdir(path, callback)

參數

參數使用說明以下:

  • path - 文件路徑。
  • callback - 回調函數,回調函數帶有兩個參數err, files,err 爲錯誤信息,files 爲 目錄下的文件數組列表。

實例

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
 
console.log("查看 /tmp 目錄");
fs.readdir("/tmp/",function(err, files){
   if (err) {
       return console.error(err);
   }
   files.forEach( function (file){
       console.log( file );
   });
});

以上代碼執行結果以下:

$ node file.js 
查看 /tmp 目錄
input.out
output.out
test
test.txt

15.11刪除目錄

語法

如下爲刪除目錄的語法格式:

fs.rmdir(path, callback)

參數

參數使用說明以下:

  • path - 文件路徑。
  • callback - 回調函數,沒有參數。

實例

接下來咱們建立 file.js 文件,代碼以下所示:

var fs = require("fs");
// 執行前建立一個空的 /tmp/test 目錄
console.log("準備刪除目錄 /tmp/test");
fs.rmdir("/tmp/test",function(err){
   if (err) {
       return console.error(err);
   }
   console.log("讀取 /tmp 目錄");
   fs.readdir("/tmp/",function(err, files){
      if (err) {
          return console.error(err);
      }
      files.forEach( function (file){
          console.log( file );
      });
   });
});

以上代碼執行結果以下:

$ node file.js 
準備刪除目錄 /tmp/test
讀取 /tmp 目錄
……

15.12文件模塊方法參考手冊

如下爲 Node.js 文件模塊相同的方法列表:

序號

方法 & 描述

1

fs.rename(oldPath, newPath, callback)
異步 rename().回調函數沒有參數,但可能拋出異常。

2

fs.ftruncate(fd, len, callback)
異步 ftruncate().回調函數沒有參數,但可能拋出異常。

3

fs.ftruncateSync(fd, len)
同步 ftruncate()

4

fs.truncate(path, len, callback)
異步 truncate().回調函數沒有參數,但可能拋出異常。

5

fs.truncateSync(path, len)
同步 truncate()

6

fs.chown(path, uid, gid, callback)
異步 chown().回調函數沒有參數,但可能拋出異常。

7

fs.chownSync(path, uid, gid)
同步 chown()

8

fs.fchown(fd, uid, gid, callback)
異步 fchown().回調函數沒有參數,但可能拋出異常。

9

fs.fchownSync(fd, uid, gid)
同步 fchown()

10

fs.lchown(path, uid, gid, callback)
異步 lchown().回調函數沒有參數,但可能拋出異常。

11

fs.lchownSync(path, uid, gid)
同步 lchown()

12

fs.chmod(path, mode, callback)
異步 chmod().回調函數沒有參數,但可能拋出異常。

13

fs.chmodSync(path, mode)
同步 chmod().

14

fs.fchmod(fd, mode, callback)
異步 fchmod().回調函數沒有參數,但可能拋出異常。

15

fs.fchmodSync(fd, mode)
同步 fchmod().

16

fs.lchmod(path, mode, callback)
異步 lchmod().回調函數沒有參數,但可能拋出異常。Only available on Mac OS X.

17

fs.lchmodSync(path, mode)
同步 lchmod().

18

fs.stat(path, callback)
異步 stat(). 回調函數有兩個參數 err, stats,stats 是 fs.Stats 對象。

19

fs.lstat(path, callback)
異步 lstat(). 回調函數有兩個參數 err, stats,stats 是 fs.Stats 對象。

20

fs.fstat(fd, callback)
異步 fstat(). 回調函數有兩個參數 err, stats,stats 是 fs.Stats 對象。

21

fs.statSync(path)
同步 stat(). 返回 fs.Stats 的實例。

22

fs.lstatSync(path)
同步 lstat(). 返回 fs.Stats 的實例。

23

fs.fstatSync(fd)
同步 fstat(). 返回 fs.Stats 的實例。

24

fs.link(srcpath, dstpath, callback)
異步 link().回調函數沒有參數,但可能拋出異常。

25

fs.linkSync(srcpath, dstpath)
同步 link().

26

fs.symlink(srcpath, dstpath[, type], callback)
異步 symlink().回調函數沒有參數,但可能拋出異常。 type 參數能夠設置爲 'dir', 'file', 或 'junction' (默認爲 'file') 。

27

fs.symlinkSync(srcpath, dstpath[, type])
同步 symlink().

28

fs.readlink(path, callback)
異步 readlink(). 回調函數有兩個參數 err, linkString。

29

fs.realpath(path[, cache], callback)
異步 realpath(). 回調函數有兩個參數 err, resolvedPath。

30

fs.realpathSync(path[, cache])
同步 realpath()。返回絕對路徑。

31

fs.unlink(path, callback)
異步 unlink().回調函數沒有參數,但可能拋出異常。

32

fs.unlinkSync(path)
同步 unlink().

33

fs.rmdir(path, callback)
異步 rmdir().回調函數沒有參數,但可能拋出異常。

34

fs.rmdirSync(path)
同步 rmdir().

35

fs.mkdir(path[, mode], callback)
S異步 mkdir(2).回調函數沒有參數,但可能拋出異常。 mode defaults to 0777.

36

fs.mkdirSync(path[, mode])
同步 mkdir().

37

fs.readdir(path, callback)
異步 readdir(3). 讀取目錄的內容。

38

fs.readdirSync(path)
同步 readdir().返回文件數組列表。

39

fs.close(fd, callback)
異步 close().回調函數沒有參數,但可能拋出異常。

40

fs.closeSync(fd)
同步 close().

41

fs.open(path, flags[, mode], callback)
異步打開文件。

42

fs.openSync(path, flags[, mode])
同步 version of fs.open().

43

fs.utimes(path, atime, mtime, callback)
 

44

fs.utimesSync(path, atime, mtime)
修改文件時間戳,文件經過指定的文件路徑。

45

fs.futimes(fd, atime, mtime, callback)
 

46

fs.futimesSync(fd, atime, mtime)
修改文件時間戳,經過文件描述符指定。

47

fs.fsync(fd, callback)
異步 fsync.回調函數沒有參數,但可能拋出異常。

48

fs.fsyncSync(fd)
同步 fsync.

49

fs.write(fd, buffer, offset, length[, position], callback)
將緩衝區內容寫入到經過文件描述符指定的文件。

50

fs.write(fd, data[, position[, encoding]], callback)
經過文件描述符 fd 寫入文件內容。

51

fs.writeSync(fd, buffer, offset, length[, position])
同步版的 fs.write()。

52

fs.writeSync(fd, data[, position[, encoding]])
同步版的 fs.write().

53

fs.read(fd, buffer, offset, length, position, callback)
經過文件描述符 fd 讀取文件內容。

54

fs.readSync(fd, buffer, offset, length, position)
同步版的 fs.read.

55

fs.readFile(filename[, options], callback)
異步讀取文件內容。

56

fs.readFileSync(filename[, options])<="" td="">

57

fs.writeFile(filename, data[, options], callback)
異步寫入文件內容。

58

fs.writeFileSync(filename, data[, options])
同步版的 fs.writeFile。

59

fs.appendFile(filename, data[, options], callback)
異步追加文件內容。

60

fs.appendFileSync(filename, data[, options])
The 同步 version of fs.appendFile.

61

fs.watchFile(filename[, options], listener)
查看文件的修改。

62

fs.unwatchFile(filename[, listener])
中止查看 filename 的修改。

63

fs.watch(filename[, options][, listener])
查看 filename 的修改,filename 能夠是文件或目錄。返回 fs.FSWatcher 對象。

64

fs.exists(path, callback)
檢測給定的路徑是否存在。

65

fs.existsSync(path)
同步版的 fs.exists.

66

fs.access(path[, mode], callback)
測試指定路徑用戶權限。

67

fs.accessSync(path[, mode])
同步版的 fs.access。

68

fs.createReadStream(path[, options])
返回ReadStream 對象。

69

fs.createWriteStream(path[, options])
返回 WriteStream 對象。

70

fs.symlink(srcpath, dstpath[, type], callback)
異步 symlink().回調函數沒有參數,但可能拋出異常。

十6、Node.js GET/POST請求

在不少場景中,咱們的服務器都須要跟用戶的瀏覽器打交道,如表單提交。

表單提交到服務器通常都使用 GET/POST 請求。

本章節咱們將爲你們介紹 Node.js GET/POS T請求。


16.1獲取GET請求內容

因爲GET請求直接被嵌入在路徑中,URL是完整的請求路徑,包括了?後面的部分,所以你能夠手動解析後面的內容做爲GET請求的參數。

node.js 中 url 模塊中的 parse 函數提供了這個功能。

實例

var http = require('http'); var url = require('url'); var util = require('util'); http.createServer(function(req, res){ res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'}); res.end(util.inspect(url.parse(req.url, true))); }).listen(3000);

在瀏覽器中訪問 http://localhost:3000/user?name=菜鳥教程&url=www.runoob.com 而後查看返回結果:

 

16.2獲取 URL 的參數

咱們可使用 url.parse 方法來解析 URL 中的參數,代碼以下:

實例

var http = require('http'); var url = require('url'); var util = require('util'); http.createServer(function(req, res){ res.writeHead(200, {'Content-Type': 'text/plain'}); // 解析 url 參數 var params = url.parse(req.url, true).query; res.write("網站名:" + params.name); res.write("\n"); res.write("網站 URL:" + params.url); res.end(); }).listen(3000);

在瀏覽器中訪問 http://localhost:3000/user?name=菜鳥教程&url=www.runoob.com 而後查看返回結果:

 


16.3獲取 POST 請求內容

POST 請求的內容所有的都在請求體中,http.ServerRequest 並無一個屬性內容爲請求體,緣由是等待請求體傳輸多是一件耗時的工做。

好比上傳文件,而不少時候咱們可能並不須要理會請求體的內容,惡意的POST請求會大大消耗服務器的資源,因此 node.js 默認是不會解析請求體的,當你須要的時候,須要手動來作。

基本語法結構說明

var http = require('http'); var querystring = require('querystring'); http.createServer(function(req, res){ // 定義了一個post變量,用於暫存請求體的信息 var post = ''; // 經過req的data事件監聽函數,每當接受到請求體的數據,就累加到post變量中 req.on('data', function(chunk){ post += chunk; }); // 在end事件觸發後,經過querystring.parse將post解析爲真正的POST請求格式,而後向客戶端返回。 req.on('end', function(){ post = querystring.parse(post); res.end(util.inspect(post)); }); }).listen(3000);

如下實例表單經過 POST 提交併輸出數據:

實例

var http = require('http'); var querystring = require('querystring'); var postHTML = '<html><head><meta charset="utf-8"><title>菜鳥教程 Node.js 實例</title></head>' + '<body>' + '<form method="post">' + '網站名: <input name="name"><br>' + '網站 URL: <input name="url"><br>' + '<input type="submit">' + '</form>' + '</body></html>'; http.createServer(function (req, res) { var body = ""; req.on('data', function (chunk) { body += chunk; }); req.on('end', function () { // 解析參數 body = querystring.parse(body); // 設置響應頭部信息及編碼 res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'}); if(body.name && body.url) { // 輸出提交的數據 res.write("網站名:" + body.name); res.write("<br>"); res.write("網站 URL:" + body.url); } else { // 輸出表單 res.write(postHTML); } res.end(); }); }).listen(3000);

執行結果 Gif 演示:

 

十7、Node.js Web 模塊


17.1什麼是 Web 服務器?

Web服務器通常指網站服務器,是指駐留於因特網上某種類型計算機的程序,Web服務器的基本功能就是提供Web信息瀏覽服務。它只需支持HTTP協議、HTML文檔格式及URL,與客戶端的網絡瀏覽器配合。

大多數 web 服務器都支持服務端的腳本語言(php、python、ruby)等,並經過腳本語言從數據庫獲取數據,將結果返回給客戶端瀏覽器。

目前最主流的三個Web服務器是Apache、Nginx、IIS。


17.2 Web 應用架構

 

  • Client - 客戶端,通常指瀏覽器,瀏覽器能夠經過 HTTP 協議向服務器請求數據。
  • Server - 服務端,通常指 Web 服務器,能夠接收客戶端請求,並向客戶端發送響應數據。
  • Business - 業務層, 經過 Web 服務器處理應用程序,如與數據庫交互,邏輯運算,調用外部程序等。
  • Data - 數據層,通常由數據庫組成。

17.3使用 Node 建立 Web 服務器

Node.js 提供了 http 模塊,http 模塊主要用於搭建 HTTP 服務端和客戶端,使用 HTTP 服務器或客戶端功能必須調用 http 模塊,代碼以下:

var http = require('http');

如下是演示一個最基本的 HTTP 服務器架構(使用 8080 端口),建立 server.js 文件,代碼以下所示:

實例

var http = require('http'); var fs = require('fs'); var url = require('url'); // 建立服務器 http.createServer( function (request, response) { // 解析請求,包括文件名 var pathname = url.parse(request.url).pathname; // 輸出請求的文件名 console.log("Request for " + pathname + " received."); // 從文件系統中讀取請求的文件內容 fs.readFile(pathname.substr(1), function (err, data) { if (err) { console.log(err); // HTTP 狀態碼: 404 : NOT FOUND // Content Type: text/plain response.writeHead(404, {'Content-Type': 'text/html'}); }else{ // HTTP 狀態碼: 200 : OK // Content Type: text/plain response.writeHead(200, {'Content-Type': 'text/html'}); // 響應文件內容 response.write(data.toString()); } // 發送響應數據 response.end(); }); }).listen(8080); // 控制檯會輸出如下信息 console.log('Server running at http://127.0.0.1:8080/');

接下來咱們在該目錄下建立一個 index.html 文件,代碼以下:

index.html 文件

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>菜鳥教程(runoob.com)</title> </head> <body> <h1>個人第一個標題</h1> <p>個人第一個段落。</p> </body> </html>

執行 server.js 文件:

$ node server.js
Server running at http://127.0.0.1:8080/

接着咱們在瀏覽器中打開地址:http://127.0.0.1:8080/index.html,顯示以下圖所示:

 

執行 server.js 的控制檯輸出信息以下:

Server running at http://127.0.0.1:8080/
Request for /index.html received.     #  客戶端請求信息

17.4使用 Node 建立 Web 客戶端

Node 建立 Web 客戶端須要引入 http 模塊,建立 client.js 文件,代碼以下所示:

實例

var http = require('http'); // 用於請求的選項 var options = { host: 'localhost', port: '8080', path: '/index.html' }; // 處理響應的回調函數 var callback = function(response){ // 不斷更新數據 var body = ''; response.on('data', function(data) { body += data; }); response.on('end', function() { // 數據接收完成 console.log(body); }); } // 向服務端發送請求 var req = http.request(options, callback); req.end();

新開一個終端,執行 client.js 文件,輸出結果以下:

$ node  client.js 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鳥教程(runoob.com)</title>
</head>
<body>
    <h1>個人第一個標題</h1>
    <p>個人第一個段落。</p>
</body>
</html>

執行 server.js 的控制檯輸出信息以下:

Server running at http://127.0.0.1:8080/
Request for /index.html received.   # 客戶端請求信息

十8、Node.js Express 框架


18.1Express 簡介

Express 是一個簡潔而靈活的 node.js Web應用框架, 提供了一系列強大特性幫助你建立各類 Web 應用,和豐富的 HTTP 工具。

使用 Express 能夠快速地搭建一個完整功能的網站。

Express 框架核心特性:

  • 能夠設置中間件來響應 HTTP 請求。
  • 定義了路由表用於執行不一樣的 HTTP 請求動做。
  • 能夠經過向模板傳遞參數來動態渲染 HTML 頁面。

18.2安裝 Express

安裝 Express 並將其保存到依賴列表中:

$ cnpm install express --save

以上命令會將 Express 框架安裝在當前目錄的 node_modules 目錄中, node_modules 目錄下會自動建立 express 目錄。如下幾個重要的模塊是須要與 express 框架一塊兒安裝的:

  • body-parser - node.js 中間件,用於處理 JSON, Raw, Text 和 URL 編碼的數據。
  • cookie-parser - 這就是一個解析Cookie的工具。經過req.cookies能夠取到傳過來的cookie,並把它們轉成對象。
  • multer - node.js 中間件,用於處理 enctype="multipart/form-data"(設置表單的MIME編碼)的表單數據。
$ cnpm install body-parser --save
$ cnpm install cookie-parser --save
$ cnpm install multer --save

安裝完後,咱們能夠查看下 express 使用的版本號:

$ cnpm list express
/data/www/node
└── express@4.15.2  -> /Users/tianqixin/www/node/node_modules/.4.15.2@express

18.3第一個 Express 框架實例

接下來咱們使用 Express 框架來輸出 "Hello World"。

如下實例中咱們引入了 express 模塊,並在客戶端發起請求後,響應 "Hello World" 字符串。

建立 express_demo.js 文件,代碼以下所示:

express_demo.js 文件代碼:

//express_demo.js 文件 var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello World'); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("應用實例,訪問地址爲 http://%s:%s", host, port) })

執行以上代碼:

$ node express_demo.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

在瀏覽器中訪問 http://127.0.0.1:8081,結果以下圖所示:

 


18.4請求和響應

Express 應用使用回調函數的參數: request 和 response 對象來處理請求和響應的數據。

app.get('/', function (req, res) {
   // --
})

request 和 response 對象的具體介紹:

Request 對象 - request 對象表示 HTTP 請求,包含了請求查詢字符串,參數,內容,HTTP 頭部等屬性。常見屬性有:

  1. req.app:當callback爲外部文件時,用req.app訪問express的實例
  2. req.baseUrl:獲取路由當前安裝的URL路徑
  3. req.body / req.cookies:得到「請求主體」/ Cookies
  4. req.fresh / req.stale:判斷請求是否還「新鮮」
  5. req.hostname / req.ip:獲取主機名和IP地址
  6. req.originalUrl:獲取原始請求URL
  7. req.params:獲取路由的parameters
  8. req.path:獲取請求路徑
  9. req.protocol:獲取協議類型
  10. req.query:獲取URL的查詢參數串
  11. req.route:獲取當前匹配的路由
  12. req.subdomains:獲取子域名
  13. req.accepts():檢查可接受的請求的文檔類型
  14. req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages:返回指定字符集的第一個可接受字符編碼
  15. req.get():獲取指定的HTTP請求頭
  16. req.is():判斷請求頭Content-Type的MIME類型

Response 對象 - response 對象表示 HTTP 響應,即在接收到請求時向客戶端發送的 HTTP 響應數據。常見屬性有:

  1. res.app:同req.app同樣
  2. res.append():追加指定HTTP頭
  3. res.set()在res.append()後將重置以前設置的頭
  4. res.cookie(name,value [,option]):設置Cookie
  5. opition: domain / expires / httpOnly / maxAge / path / secure / signed
  6. res.clearCookie():清除Cookie
  7. res.download():傳送指定路徑的文件
  8. res.get():返回指定的HTTP頭
  9. res.json():傳送JSON響應
  10. res.jsonp():傳送JSONP響應
  11. res.location():只設置響應的Location HTTP頭,不設置狀態碼或者close response
  12. res.redirect():設置響應的Location HTTP頭,而且設置狀態碼302
  13. res.render(view,[locals],callback):渲染一個view,同時向callback傳遞渲染後的字符串,若是在渲染過程當中有錯誤發生next(err)將會被自動調用。callback將會被傳入一個可能發生的錯誤以及渲染後的頁面,這樣就不會自動輸出了。
  14. res.send():傳送HTTP響應
  15. res.sendFile(path [,options] [,fn]):傳送指定路徑的文件 -會自動根據文件extension設定Content-Type
  16. res.set():設置HTTP頭,傳入object能夠一次設置多個頭
  17. res.status():設置HTTP狀態碼
  18. res.type():設置Content-Type的MIME類型

18.5路由

咱們已經瞭解了 HTTP 請求的基本應用,而路由決定了由誰(指定腳本)去響應客戶端請求。

在HTTP請求中,咱們能夠經過路由提取出請求的URL以及GET/POST參數。

接下來咱們擴展 Hello World,添加一些功能來處理更多類型的 HTTP 請求。

建立 express_demo2.js 文件,代碼以下所示:

express_demo2.js 文件代碼:

var express = require('express'); var app = express(); // 主頁輸出 "Hello World" app.get('/', function (req, res) { console.log("主頁 GET 請求"); res.send('Hello GET'); }) // POST 請求 app.post('/', function (req, res) { console.log("主頁 POST 請求"); res.send('Hello POST'); }) // /del_user 頁面響應 app.get('/del_user', function (req, res) { console.log("/del_user 響應 DELETE 請求"); res.send('刪除頁面'); }) // /list_user 頁面 GET 請求 app.get('/list_user', function (req, res) { console.log("/list_user GET 請求"); res.send('用戶列表頁面'); }) // 對頁面 abcd, abxcd, ab123cd, 等響應 GET 請求 app.get('/ab*cd', function(req, res) { console.log("/ab*cd GET 請求"); res.send('正則匹配'); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("應用實例,訪問地址爲 http://%s:%s", host, port) })

執行以上代碼:

$ node express_demo2.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

接下來你能夠嘗試訪問 http://127.0.0.1:8081 不一樣的地址,查看效果。

在瀏覽器中訪問 http://127.0.0.1:8081/list_user,結果以下圖所示:

 

在瀏覽器中訪問 http://127.0.0.1:8081/abcd,結果以下圖所示:

 

在瀏覽器中訪問 http://127.0.0.1:8081/abcdefg,結果以下圖所示:

 


18.6靜態文件

Express 提供了內置的中間件 express.static 來設置靜態文件如:圖片, CSS, JavaScript 等。

你可使用 express.static 中間件來設置靜態文件路徑。例如,若是你將圖片, CSS, JavaScript 文件放在 public 目錄下,你能夠這麼寫:

app.use(express.static('public'));

咱們能夠到 public/images 目錄下放些圖片,以下所示:

 
項目路徑/public/images/logo.png

讓咱們再修改下 "Hello World" 應用添加處理靜態文件的功能。

建立 express_demo3.js 文件,代碼以下所示:

express_demo3.js 文件代碼:

var express = require('express'); var app = express(); app.use(express.static('public')); app.get('/', function (req, res) { res.send('Hello World'); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("應用實例,訪問地址爲 http://%s:%s", host, port) })

執行以上代碼:

$ node express_demo3.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

執行以上代碼:

在瀏覽器中訪問 http://127.0.0.1:8081/images/logo.png(本實例採用了菜鳥教程的logo),結果以下圖所示:

 


18.7GET 方法

如下實例演示了在表單中經過 GET 方法提交兩個參數,咱們可使用 server.js 文件內的 process_get 路由器來處理輸入:

index.htm 文件代碼:(文件放在public文件夾下)

<html> <body> <form action="http://127.0.0.1:8081/process_get" method="GET"> First Name: <input type="text" name="first_name"> <br> Last Name: <input type="text" name="last_name"> <input type="submit" value="Submit"> </form> </body> </html>

 

server.js 文件代碼:

var express = require('express'); var app = express(); app.use(express.static('public')); app.get('/index.htm', function (req, res) { res.sendFile( __dirname + "/" + "index.htm" ); }) app.get('/process_get', function (req, res) { // 輸出 JSON 格式 var response = { "first_name":req.query.first_name, "last_name":req.query.last_name }; console.log(response); res.end(JSON.stringify(response)); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("應用實例,訪問地址爲 http://%s:%s", host, port) })

執行以上代碼:

node server.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

瀏覽器訪問 http://127.0.0.1:8081/index.htm,如圖所示:

 

如今你能夠向表單輸入數據,並提交,以下演示:

 


18.8POST 方法

如下實例演示了在表單中經過 POST 方法提交兩個參數,咱們可使用 server.js 文件內的 process_post 路由器來處理輸入:

index.htm 文件代碼:

<html> <body> <form action="http://127.0.0.1:8081/process_post" method="POST"> First Name: <input type="text" name="first_name"> <br> Last Name: <input type="text" name="last_name"> <input type="submit" value="Submit"> </form> </body> </html>

 

server.js 文件代碼:

var express = require('express'); var app = express(); var bodyParser = require('body-parser'); // 建立 application/x-www-form-urlencoded 編碼解析 var urlencodedParser = bodyParser.urlencoded({ extended: false }) app.use(express.static('public')); app.get('/index.htm', function (req, res) { res.sendFile( __dirname + "/" + "index.htm" ); }) app.post('/process_post', urlencodedParser, function (req, res) { // 輸出 JSON 格式 var response = { "first_name":req.body.first_name, "last_name":req.body.last_name }; console.log(response); res.end(JSON.stringify(response)); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("應用實例,訪問地址爲 http://%s:%s", host, port) })

執行以上代碼:

$ node server.js
應用實例,訪問地址爲 http://0.0.0.0:8081

瀏覽器訪問 http://127.0.0.1:8081/index.htm,如圖所示:

 

如今你能夠向表單輸入數據,並提交,以下演示:

 


18.9文件上傳

如下咱們建立一個用於上傳文件的表單,使用 POST 方法,表單 enctype 屬性設置爲 multipart/form-data。

index.htm 文件代碼:

<html> <head> <title>文件上傳表單</title> </head> <body> <h3>文件上傳:</h3> 選擇一個文件上傳: <br /> <form action="/file_upload" method="post" enctype="multipart/form-data"> <input type="file" name="image" size="50" /> <br /> <input type="submit" value="上傳文件" /> </form> </body> </html>

 

server.js 文件代碼:

var express = require('express'); var app = express(); var fs = require("fs"); var bodyParser = require('body-parser'); var multer = require('multer'); app.use(express.static('public')); app.use(bodyParser.urlencoded({ extended: false })); app.use(multer({ dest: '/tmp/'}).array('image')); app.get('/index.htm', function (req, res) { res.sendFile( __dirname + "/" + "index.htm" ); }) app.post('/file_upload', function (req, res) { console.log(req.files[0]); // 上傳的文件信息 var des_file = __dirname + "/" + req.files[0].originalname; fs.readFile( req.files[0].path, function (err, data) { fs.writeFile(des_file, data, function (err) { if( err ){ console.log( err ); }else{ response = { message:'File uploaded successfully', filename:req.files[0].originalname }; } console.log( response ); res.end( JSON.stringify( response ) ); }); }); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("應用實例,訪問地址爲 http://%s:%s", host, port) })

執行以上代碼:

$ node server.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

瀏覽器訪問 http://127.0.0.1:8081/index.htm,如圖所示:

 

如今你能夠向表單輸入數據,並提交,以下演示:

 


18.10Cookie 管理

咱們可使用中間件向 Node.js 服務器發送 cookie 信息,如下代碼輸出了客戶端發送的 cookie 信息:

express_cookie.js 文件代碼:

// express_cookie.js 文件 var express = require('express') var cookieParser = require('cookie-parser') var util = require('util'); var app = express() app.use(cookieParser()) app.get('/', function(req, res) { console.log("Cookies: " + util.inspect(req.cookies)); }) app.listen(8081)

執行以上代碼:

$ node express_cookie.js 

如今你能夠訪問 http://127.0.0.1:8081 並查看終端信息的輸出,以下演示:

 

 

十9、Node.js RESTful API

19.1什麼是 REST?

REST即表述性狀態傳遞(英文:Representational State Transfer,簡稱REST)是Roy Fielding博士在2000年他的博士論文中提出來的一種軟件架構風格。

表述性狀態轉移是一組架構約束條件和原則。知足這些約束條件和原則的應用程序或設計就是RESTful。須要注意的是,REST是設計風格而不是標準。REST一般基於使用HTTP,URI,和XML(標準通用標記語言下的一個子集)以及HTML(標準通用標記語言下的一個應用)這些現有的普遍流行的協議和標準。REST 一般使用 JSON 數據格式。

19.2 HTTP 方法

如下爲 REST 基本架構的四個方法:

  • GET - 用於獲取數據。
  • PUT - 用於更新或添加數據。
  • DELETE - 用於刪除數據。
  • POST - 用於添加數據。

19.3 RESTful Web Services

Web service是一個平臺獨立的,低耦合的,自包含的、基於可編程的web的應用程序,可以使用開放的XML(標準通用標記語言下的一個子集)標準來描述、發佈、發現、協調和配置這些應用程序,用於開發分佈式的互操做的應用程序。

基於 REST 架構的 Web Services 便是 RESTful。

因爲輕量級以及經過 HTTP 直接傳輸數據的特性,Web 服務的 RESTful 方法已經成爲最多見的替代方法。可使用各類語言(好比 Java 程序、Perl、Ruby、Python、PHP 和 Javascript[包括 Ajax])實現客戶端。

RESTful Web 服務一般能夠經過自動客戶端或表明用戶的應用程序訪問。可是,這種服務的簡便性讓用戶可以與之直接交互,使用它們的 Web 瀏覽器構建一個 GET URL 並讀取返回的內容。

更多介紹,能夠查看:RESTful 架構詳解


19.4建立 RESTful

首先,建立一個 json 數據資源文件 users.json,內容以下:

{
   "user1" : {
      "name" : "mahesh",
      "password" : "password1",
      "profession" : "teacher",
      "id": 1
   },
   "user2" : {
      "name" : "suresh",
      "password" : "password2",
      "profession" : "librarian",
      "id": 2
   },
   "user3" : {
      "name" : "ramesh",
      "password" : "password3",
      "profession" : "clerk",
      "id": 3
   }
}

基於以上數據,咱們建立如下 RESTful API:

序號

URI

HTTP 方法

發送內容

結果

1

listUsers

GET

顯示全部用戶列表

2

addUser

POST

JSON 字符串

添加新用戶

3

deleteUser

DELETE

JSON 字符串

刪除用戶

4

:id

GET

顯示用戶詳細信息

19.4.1獲取用戶列表:

如下代碼,咱們建立了 RESTful API listUsers,用於讀取用戶的信息列表, server.js 文件代碼以下所示:

var express = require('express');
var app = express();
var fs = require("fs");
 
app.get('/listUsers', function (req, res) {
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       console.log( data );
       res.end( data );
   });
})
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
 
  console.log("應用實例,訪問地址爲 http://%s:%s", host, port)
 
})

接下來執行如下命令:

$ node server.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

在瀏覽器中訪問 http://127.0.0.1:8081/listUsers,結果以下所示:

{
   "user1" : {
      "name" : "mahesh",
      "password" : "password1",
      "profession" : "teacher",
      "id": 1
   },
   "user2" : {
      "name" : "suresh",
      "password" : "password2",
      "profession" : "librarian",
      "id": 2
   },
   "user3" : {
      "name" : "ramesh",
      "password" : "password3",
      "profession" : "clerk",
      "id": 3
   }
}

19.4.2添加用戶

如下代碼,咱們建立了 RESTful API addUser, 用於添加新的用戶數據,server.js 文件代碼以下所示:

var express = require('express');
var app = express();
var fs = require("fs");
 
//添加的新用戶數據
var user = {
   "user4" : {
      "name" : "mohit",
      "password" : "password4",
      "profession" : "teacher",
      "id": 4
   }
}
 
app.get('/addUser', function (req, res) {
   // 讀取已存在的數據
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       data = JSON.parse( data );
       data["user4"] = user["user4"];
       console.log( data );
       res.end( JSON.stringify(data));
   });
})
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
  console.log("應用實例,訪問地址爲 http://%s:%s", host, port)
 
})

接下來執行如下命令:

$ node server.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

在瀏覽器中訪問 http://127.0.0.1:8081/addUser,結果以下所示:

{ user1:
   { name: 'mahesh',
     password: 'password1',
     profession: 'teacher',
     id: 1 },
  user2:
   { name: 'suresh',
     password: 'password2',
     profession: 'librarian',
     id: 2 },
  user3:
   { name: 'ramesh',
     password: 'password3',
     profession: 'clerk',
     id: 3 },
  user4:
   { name: 'mohit',
     password: 'password4',
     profession: 'teacher',
     id: 4 } 
}

19.4.3顯示用戶詳情

如下代碼,咱們建立了 RESTful API :id(用戶id, 用於讀取指定用戶的詳細信息,server.js 文件代碼以下所示:

var express = require('express');
var app = express();
var fs = require("fs");
 
app.get('/:id', function (req, res) {
   // 首先咱們讀取已存在的用戶
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       data = JSON.parse( data );
       var user = data["user" + req.params.id] 
       console.log( user );
       res.end( JSON.stringify(user));
   });
})
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
  console.log("應用實例,訪問地址爲 http://%s:%s", host, port)
 
})

接下來執行如下命令:

$ node server.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

在瀏覽器中訪問 http://127.0.0.1:8081/2,結果以下所示:

{
   "name":"suresh",
   "password":"password2",
   "profession":"librarian",
   "id":2
}

19.4.4刪除用戶

如下代碼,咱們建立了 RESTful API deleteUser, 用於刪除指定用戶的詳細信息,如下實例中,用戶 id 爲 2,server.js 文件代碼以下所示:

var express = require('express');
var app = express();
var fs = require("fs");
 
var id = 2;
 
app.get('/deleteUser', function (req, res) {
 
   // First read existing users.
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       data = JSON.parse( data );
       delete data["user" + 2];
       
       console.log( data );
       res.end( JSON.stringify(data));
   });
})
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
  console.log("應用實例,訪問地址爲 http://%s:%s", host, port)
 
})

接下來執行如下命令:

$ node server.js 
應用實例,訪問地址爲 http://0.0.0.0:8081

在瀏覽器中訪問 http://127.0.0.1:8081/deleteUser,結果以下所示:

{ user1:
   { name: 'mahesh',
     password: 'password1',
     profession: 'teacher',
     id: 1 },
  user3:
   { name: 'ramesh',
     password: 'password3',
     profession: 'clerk',
     id: 3 } 
}

 

二10、Node.js 鏈接 MySQL

本章節咱們將爲你們介紹如何使用 Node.js 來鏈接 MySQL,並對數據庫進行操做。

若是你尚未 MySQL 的基本知識,能夠參考咱們的教程:MySQL 教程

本教程使用到的 Websites 表 SQL 文件:websites.sql

20.1安裝驅動

本教程使用了淘寶定製的 cnpm 命令進行安裝:

$ cnpm install mysql

20.2鏈接數據庫

在如下實例中根據你的實際配置修改數據庫用戶名、及密碼及數據庫名:

test.js 文件代碼:

var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', database : 'test' }); connection.connect(); connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) { if (error) throw error; console.log('The solution is: ', results[0].solution); });

執行如下命令輸出就結果爲:

$ node test.js
The solution is: 2

20.3數據庫鏈接參數說明:

參數

描述

host

主機地址 (默認:localhost)

  user

用戶名

  password

密碼

  port

端口號 (默認:3306)

  database

數據庫名

  charset

鏈接字符集(默認:'UTF8_GENERAL_CI',注意字符集的字母都要大寫)

  localAddress

此IP用於TCP鏈接(可選)

  socketPath

鏈接到unix域路徑,當使用 host 和 port 時會被忽略

  timezone

時區(默認:'local')

  connectTimeout

鏈接超時(默認:不限制;單位:毫秒)

  stringifyObjects

是否序列化對象

  typeCast

是否將列值轉化爲本地JavaScript類型值 (默認:true)

  queryFormat

自定義query語句格式化方法

  supportBigNumbers

數據庫支持bigint或decimal類型列時,須要設此option爲true (默認:false)

  bigNumberStrings

supportBigNumbers和bigNumberStrings啓用 強制bigint或decimal列以JavaScript字符串類型返回(默認:false)

  dateStrings

強制timestamp,datetime,data類型以字符串類型返回,而不是JavaScript Date類型(默認:false)

  debug

開啓調試(默認:false)

  multipleStatements

是否許一個query中有多個MySQL語句 (默認:false)

  flags

用於修改鏈接標誌

  ssl

使用ssl參數(與crypto.createCredenitals參數格式一至)或一個包含ssl配置文件名稱的字符串,目前只捆綁Amazon RDS的配置文件

更多說明可參見:https://github.com/mysqljs/mysql


20.4數據庫操做( CURD )

在進行數據庫操做前,你須要將本站提供的 Websites 表 SQL 文件websites.sql 導入到你的 MySQL 數據庫中。

本教程測試的 MySQL 用戶名爲 root,密碼爲 123456,數據庫爲 test,你須要根據本身配置狀況修改。

20.4.1查詢數據

將上面咱們提供的 SQL 文件導入數據庫後,執行如下代碼便可查詢出數據:

查詢數據

var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', port: '3306', database: 'test', }); connection.connect(); var sql = 'SELECT * FROM websites'; //查 connection.query(sql,function (err, result) { if(err){ console.log('[SELECT ERROR] - ',err.message); return; } console.log('--------------------------SELECT----------------------------'); console.log(result); console.log('------------------------------------------------------------\n\n'); }); connection.end();

執行如下命令輸出就結果爲:

$ node test.js
--------------------------SELECT----------------------------
[ RowDataPacket {
    id: 1,
    name: 'Google',
    url: 'https://www.google.cm/',
    alexa: 1,
    country: 'USA' },
  RowDataPacket {
    id: 2,
    name: '淘寶',
    url: 'https://www.taobao.com/',
    alexa: 13,
    country: 'CN' },
  RowDataPacket {
    id: 3,
    name: '菜鳥教程',
    url: 'http://www.runoob.com/',
    alexa: 4689,
    country: 'CN' },
  RowDataPacket {
    id: 4,
    name: '微博',
    url: 'http://weibo.com/',
    alexa: 20,
    country: 'CN' },
  RowDataPacket {
    id: 5,
    name: 'Facebook',
    url: 'https://www.facebook.com/',
    alexa: 3,
    country: 'USA' } ]
------------------------------------------------------------

20.4.2插入數據

咱們能夠向數據表 websties 插入數據:

插入數據

var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', port: '3306', database: 'test', }); connection.connect(); var addSql = 'INSERT INTO websites(Id,name,url,alexa,country) VALUES(0,?,?,?,?)'; var addSqlParams = ['菜鳥工具', 'https://c.runoob.com','23453', 'CN']; //增 connection.query(addSql,addSqlParams,function (err, result) { if(err){ console.log('[INSERT ERROR] - ',err.message); return; } console.log('--------------------------INSERT----------------------------'); //console.log('INSERT ID:',result.insertId); console.log('INSERT ID:',result); console.log('-----------------------------------------------------------------\n\n'); }); connection.end();

執行如下命令輸出就結果爲:

$ node test.js
--------------------------INSERT----------------------------
INSERT ID: OkPacket {
  fieldCount: 0,
  affectedRows: 1,
  insertId: 6,
  serverStatus: 2,
  warningCount: 0,
  message: '',
  protocol41: true,
  changedRows: 0 }
-----------------------------------------------------------------

執行成功後,查看數據表,便可以看到添加的數據:

 

20.4.3更新數據

咱們也能夠對數據庫的數據進行修改:

更新數據

var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', port: '3306', database: 'test', }); connection.connect(); var modSql = 'UPDATE websites SET name = ?,url = ? WHERE Id = ?'; var modSqlParams = ['菜鳥移動站', 'https://m.runoob.com',6]; //改 connection.query(modSql,modSqlParams,function (err, result) { if(err){ console.log('[UPDATE ERROR] - ',err.message); return; } console.log('--------------------------UPDATE----------------------------'); console.log('UPDATE affectedRows',result.affectedRows); console.log('-----------------------------------------------------------------\n\n'); }); connection.end();

執行如下命令輸出就結果爲:

--------------------------UPDATE----------------------------
UPDATE affectedRows 1
-----------------------------------------------------------------

執行成功後,查看數據表,便可以看到更新的數據:

 

20.4.4刪除數據

咱們可使用如下代碼來刪除 id 爲 6 的數據:

刪除數據

var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', port: '3306', database: 'test', }); connection.connect(); var delSql = 'DELETE FROM websites where id=6'; //刪 connection.query(delSql,function (err, result) { if(err){ console.log('[DELETE ERROR] - ',err.message); return; } console.log('--------------------------DELETE----------------------------'); console.log('DELETE affectedRows',result.affectedRows); console.log('-----------------------------------------------------------------\n\n'); }); connection.end();

執行如下命令輸出就結果爲:

--------------------------DELETE----------------------------
DELETE affectedRows 1
-----------------------------------------------------------------

執行成功後,查看數據表,便可以看到 id=6 的數據已被刪除:

 

參考資料:http://www.runoob.com/nodejs/nodejs-tutorial.html 

相關文章
相關標籤/搜索