Node.js Learning Notes

簡介

簡單的說 Node.js 就是運行在服務端的 JavaScript。javascript

Node.js 是一個基於Chrome JavaScript 運行時創建的一個平臺。php

Node.js是一個事件驅動I/O服務端JavaScript環境,基於Google的V8引擎,V8引擎執行Javascript的速度很是快,性能很是好。html

安裝

下載地址:https://nodejs.org/en/download/java

wget https://nodejs.org/dist/v10.16.3/node-v10.16.3.tar.gz

解壓node

tar -zxvf node-v10.16.3.tar.gz

編譯python

sudo ./configure
sudo make
sudo make install

查看 node 版本:mysql

node --version

 若不在環境變量中可添加軟鏈接linux

ln -s /usr/software/nodejs/bin/npm   /usr/local/bin/ 
ln -s /usr/software/nodejs/bin/node   /usr/local/bin/

 使用

若是咱們使用PHP來編寫後端的代碼時,須要Apache 或者 Nginx 的HTTP 服務器,並配上 mod_php5 模塊和php-cgi。git

從這個角度看,整個"接收 HTTP 請求並提供 Web 頁面"的需求根本不需 要 PHP 來處理。github

不過對 Node.js 來講,概念徹底不同了。使用 Node.js 時,咱們不只僅 在實現一個應用,同時還實現了整個 HTTP 服務器。事實上,咱們的 Web 應用以及對應的 Web 服務器基本上是同樣的。

在咱們建立 Node.js 第一個 "Hello, World!" 應用前,讓咱們先了解下 Node.js 應用是由哪幾部分組成的:

  1. 引入 required 模塊:咱們可使用 require 指令來載入 Node.js 模塊。

  2. 建立服務器:服務器能夠監聽客戶端的請求,相似於 Apache 、Nginx 等 HTTP 服務器。

  3. 接收請求與響應請求 服務器很容易建立,客戶端可使用瀏覽器或終端發送 HTTP 請求,服務器接收請求後返回響應數據。

建立 Node.js 應用

步驟1、引入 required 模塊

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

var http = require("http");

步驟2、建立服務器

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

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

//引入http模塊
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 服務器監聽的端口號。

npm

簡介:

npm全稱爲Node Package Manager,是一個基於Node.js的包管理器,也是整個Node.js社區最流行、支持的第三方模塊最多的包管理器。

npm的初衷:JavaScript開發人員更容易分享和重用代碼。

npm的使用場景:

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

npm版本查詢:

npm -v 

npm安裝:

  一、安裝nodejs    因爲新版的nodejs已經集成了npm,因此可直接經過輸入npm -v來測試是否成功安裝。

  二、使用npm命令來升級npm: npm install npm -g

使用淘寶鏡像

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

使用 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');

全局安裝與本地安裝

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)
View Code

查看安裝信息

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

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

使用 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!"}
View Code

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 - 關鍵字

卸載模塊

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

npm uninstall express

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

npm ls

更新模塊

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

npm update express

搜索模塊

使用如下來搜索模塊:

npm search express

建立模塊

建立模塊,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
View Code

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

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

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

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

npm publish

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

版本號

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

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

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

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

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

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>能夠撤銷發佈本身發佈過的某個版本代碼。

使用淘寶 NPM 鏡像

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

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

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

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

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

cnpm install [name]

更多信息能夠查閱:http://npm.taobao.org/

  1. 若是你遇到了使用 npm 安 裝node_modules 老是提示報錯:報錯: npm resource busy or locked.....

    能夠先刪除之前安裝的 node_modules :

npm cache clean
npm install

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

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

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

  • 讀取 - 讀取用戶輸入,解析輸入了Javascript 數據結構並存儲在內存中。

  • 執行 - 執行輸入的數據結構

  • 打印 - 輸出結果

  • 循環 - 循環操做以上步驟直到用戶兩次按下 ctrl-c 按鈕退出。

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

開始學習 REPL

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

$ node
>

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

簡單的表達式運算

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

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

使用變量

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

變量聲明須要使用 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

多行表達式

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 會自動檢測是否爲連續的表達式。

下劃線(_)變量

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

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

REPL 命令

  • ctrl + c - 退出當前終端。

  • ctrl + c 按下兩次 - 退出 Node REPL。

  • ctrl + d - 退出 Node REPL.

  • 向上/向下 鍵 - 查看輸入的歷史命令

  • tab 鍵 - 列出當前命令

  • .help - 列出使用命令

  • .break - 退出多行表達式

  • .clear - 退出多行表達式

  • .save filename - 保存當前的 Node REPL 會話到指定文件

  • .load filename - 載入當前 Node REPL 會話的文件內容。

中止 REPL

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

$ node
>
(^C again to quit)
>

Node.js 回調函數

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

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

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

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

回調函數通常做爲函數的最後一個參數出現:

function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }

 

阻塞代碼實例

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

blocking demo test text

 

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

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

 

以上代碼執行結果以下:

非阻塞代碼實例

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

no blocking demo test text

 

建立 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.js 事件循環

Node.js 是單進程單線程應用程序,可是由於 V8 引擎提供的異步執行回調接口,經過這些接口能夠處理大量的併發,因此性能很是高。

Node.js 幾乎每個 API 都是支持回調函數的。

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

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

事件驅動程序

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');

 

實例

建立 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("程序執行完畢。");
View Code

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

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

 

Node 應用程序是如何工做的?

在 Node 應用程序中,執行異步操做的函數將回調函數做爲最後一個參數, 回調函數接收錯誤對象做爲第一個參數。

接下來讓咱們來從新看下前面的實例,建立一個 input.txt ,文件內容以下:

no blocking demo test text

 

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

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
   if (err){
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});
console.log("程序執行完畢");

 

以上程序中 fs.readFile() 是異步函數用於讀取文件。 若是在讀取文件過程當中發生錯誤,錯誤 err 對象就會輸出錯誤信息。

若是沒發生錯誤,readFile 跳過 err 對象的輸出,文件內容就經過回調函數輸出。

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

程序執行完畢
no blocking demo test text 

接下來咱們刪除 input.txt 文件,執行結果以下所示:

程序執行完畢
Error: ENOENT, open 'input.txt'

 

由於文件 input.txt 不存在,因此輸出了錯誤信息。

Node.js EventEmitter

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

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

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)
返回指定事件的監聽器數量。
events.EventEmitter.listenerCount(emitter, eventName) //已廢棄,不推薦 events.emitter.listenerCount(eventName) //推薦

事件

序號 事件 & 描述
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 = eventEmitter.listenerCount('connection');
console.log(eventListeners + " 個監聽器監聽鏈接事件。");

// 處理 connection 事件 
eventEmitter.emit('connection');

// 移除監綁定的 listener1 函數
eventEmitter.removeListener('connection', listener1);
console.log("listener1 再也不受監聽。");

// 觸發鏈接事件
eventEmitter.emit('connection');

eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " 個監聽器監聽鏈接事件。");

console.log("程序執行完畢。");
View Code

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

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

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)

繼承 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對象。


Buffer 與字符編碼

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

const buf = Buffer.from('hello','ascii');
console.log(buf.toString('hex'))
//輸出:68656c6c6f
console.log(buf.toString('base64'));
// 輸出 aGVsbG8= 

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 - 將每一個字節編碼爲兩個十六進制字符。

建立 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');

寫入緩衝區

語法

寫入 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

 

從緩衝區讀取數據

語法

讀取 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

將 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>

緩衝區合併

語法

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

緩衝區比較

語法

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以前

 

拷貝緩衝區

語法

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

緩衝區裁剪

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

 

緩衝區長度

語法

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

buf.length;

 

返回值

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

實例

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

 

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

buffer length: 14

 


方法參考手冊

如下列出了 Node.js Buffer 模塊經常使用的方法(注意有些方法在舊版本是沒有的):

序號 方法 & 描述
1 new Buffer(size)
分配一個新的 size 大小單位爲8位字節的 buffer。 注意, size 必須小於 kMaxLength,不然,將會拋出異常 RangeError。廢棄的: 使用 Buffer.alloc() 代替(或 Buffer.allocUnsafe())。
2 new Buffer(buffer)
拷貝參數 buffer 的數據到 Buffer 實例。廢棄的: 使用 Buffer.from(buffer) 代替。
3 new Buffer(str[, encoding])
分配一個新的 buffer ,其中包含着傳入的 str 字符串。 encoding 編碼方式默認爲 'utf8'。 廢棄的: 使用 Buffer.from(string[, encoding]) 代替。
4 buf.length
返回這個 buffer 的 bytes 數。注意這未必是 buffer 裏面內容的大小。length 是 buffer 對象所分配的內存數,它不會隨着這個 buffer 對象內容的改變而改變。
5 buf.write(string[, offset[, length]][, encoding])
根據參數 offset 偏移量和指定的 encoding 編碼方式,將參數 string 數據寫入buffer。 offset 偏移量默認值是 0, encoding 編碼方式默認是 utf8。 length 長度是將要寫入的字符串的 bytes 大小。 返回 number 類型,表示寫入了多少 8 位字節流。若是 buffer 沒有足夠的空間來放整個 string,它將只會只寫入部分字符串。 length 默認是 buffer.length - offset。 這個方法不會出現寫入部分字符。
6 buf.writeUIntLE(value, offset, byteLength[, noAssert])
將 value 寫入到 buffer 裏, 它由 offset 和 byteLength 決定,最高支持 48 位無符號整數,小端對齊,例如:
const buf = Buffer.allocUnsafe(6); buf.writeUIntLE(0x1234567890ab, 0, 6); // 輸出: <Buffer ab 90 78 56 34 12> console.log(buf);
noAssert 值爲 true 時,再也不驗證 value 和 offset 的有效性。 默認是 false。
7 buf.writeUIntBE(value, offset, byteLength[, noAssert])
將 value 寫入到 buffer 裏, 它由 offset 和 byteLength 決定,最高支持 48 位無符號整數,大端對齊。noAssert 值爲 true 時,再也不驗證 value 和 offset 的有效性。 默認是 false。
const buf = Buffer.allocUnsafe(6); buf.writeUIntBE(0x1234567890ab, 0, 6); // 輸出: <Buffer 12 34 56 78 90 ab> console.log(buf);
8 buf.writeIntLE(value, offset, byteLength[, noAssert])
將value 寫入到 buffer 裏, 它由offset 和 byteLength 決定,最高支持48位有符號整數,小端對齊。noAssert 值爲 true 時,再也不驗證 value 和 offset 的有效性。 默認是 false。
9 buf.writeIntBE(value, offset, byteLength[, noAssert])
將value 寫入到 buffer 裏, 它由offset 和 byteLength 決定,最高支持48位有符號整數,大端對齊。noAssert 值爲 true 時,再也不驗證 value 和 offset 的有效性。 默認是 false。
10 buf.readUIntLE(offset, byteLength[, noAssert])
支持讀取 48 位如下的無符號數字,小端對齊。noAssert 值爲 true 時, offset 再也不驗證是否超過 buffer 的長度,默認爲 false。
11 buf.readUIntBE(offset, byteLength[, noAssert])
支持讀取 48 位如下的無符號數字,大端對齊。noAssert 值爲 true 時, offset 再也不驗證是否超過 buffer 的長度,默認爲 false。
12 buf.readIntLE(offset, byteLength[, noAssert])
支持讀取 48 位如下的有符號數字,小端對齊。noAssert 值爲 true 時, offset 再也不驗證是否超過 buffer 的長度,默認爲 false。
13 buf.readIntBE(offset, byteLength[, noAssert])
支持讀取 48 位如下的有符號數字,大端對齊。noAssert 值爲 true 時, offset 再也不驗證是否超過 buffer 的長度,默認爲 false。
14 buf.toString([encoding[, start[, end]]])
根據 encoding 參數(默認是 'utf8')返回一個解碼過的 string 類型。還會根據傳入的參數 start (默認是 0) 和 end (默認是 buffer.length)做爲取值範圍。
15 buf.toJSON()
將 Buffer 實例轉換爲 JSON 對象。
16 buf[index]
獲取或設置指定的字節。返回值表明一個字節,因此返回值的合法範圍是十六進制0x00到0xFF 或者十進制0至 255。
17 buf.equals(otherBuffer)
比較兩個緩衝區是否相等,若是是返回 true,不然返回 false。
18 buf.compare(otherBuffer)
比較兩個 Buffer 對象,返回一個數字,表示 buf 在 otherBuffer 以前,以後或相同。
19 buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
buffer 拷貝,源和目標能夠相同。 targetStart 目標開始偏移和 sourceStart 源開始偏移默認都是 0。 sourceEnd 源結束位置偏移默認是源的長度 buffer.length 。
20 buf.slice([start[, end]])
剪切 Buffer 對象,根據 start(默認是 0 ) 和 end (默認是 buffer.length ) 偏移和裁剪了索引。 負的索引是從 buffer 尾部開始計算的。
21 buf.readUInt8(offset[, noAssert])
根據指定的偏移量,讀取一個無符號 8 位整數。若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 若是這樣 offset 可能會超出buffer 的末尾。默認是 false。
22 buf.readUInt16LE(offset[, noAssert])
根據指定的偏移量,使用特殊的 endian 字節序格式讀取一個無符號 16 位整數。若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出 buffer 的末尾。默認是 false。
23 buf.readUInt16BE(offset[, noAssert])
根據指定的偏移量,使用特殊的 endian 字節序格式讀取一個無符號 16 位整數,大端對齊。若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出 buffer 的末尾。默認是 false。
24 buf.readUInt32LE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 字節序格式讀取一個無符號 32 位整數,小端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出buffer 的末尾。默認是 false。
25 buf.readUInt32BE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 字節序格式讀取一個無符號 32 位整數,大端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出buffer 的末尾。默認是 false。
26 buf.readInt8(offset[, noAssert])
根據指定的偏移量,讀取一個有符號 8 位整數。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出 buffer 的末尾。默認是 false。
27 buf.readInt16LE(offset[, noAssert])
根據指定的偏移量,使用特殊的 endian 格式讀取一個 有符號 16 位整數,小端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出 buffer 的末尾。默認是 false。
28 buf.readInt16BE(offset[, noAssert])
根據指定的偏移量,使用特殊的 endian 格式讀取一個 有符號 16 位整數,大端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出 buffer 的末尾。默認是 false。
29 buf.readInt32LE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 字節序格式讀取一個有符號 32 位整數,小端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出buffer 的末尾。默認是 false。
30 buf.readInt32BE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 字節序格式讀取一個有符號 32 位整數,大端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出buffer 的末尾。默認是 false。
31 buf.readFloatLE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 字節序格式讀取一個 32 位雙浮點數,小端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出buffer的末尾。默認是 false。
32 buf.readFloatBE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian 字節序格式讀取一個 32 位雙浮點數,大端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出buffer的末尾。默認是 false。
33 buf.readDoubleLE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian字節序格式讀取一個 64 位雙精度數,小端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出buffer 的末尾。默認是 false。
34 buf.readDoubleBE(offset[, noAssert])
根據指定的偏移量,使用指定的 endian字節序格式讀取一個 64 位雙精度數,大端對齊。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 offset 可能會超出buffer 的末尾。默認是 false。
35 buf.writeUInt8(value, offset[, noAssert])
根據傳入的 offset 偏移量將 value 寫入 buffer。注意:value 必須是一個合法的無符號 8 位整數。 若參數 noAssert 爲 true 將不會驗證 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然不要使用。默認是 false。
36 buf.writeUInt16LE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的無符號 16 位整數,小端對齊。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出buffer的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
37 buf.writeUInt16BE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的無符號 16 位整數,大端對齊。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出buffer的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
38 buf.writeUInt32LE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式(LITTLE-ENDIAN:小字節序)將 value 寫入buffer。注意:value 必須是一個合法的無符號 32 位整數,小端對齊。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着value 可能過大,或者offset可能會超出buffer的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
39 buf.writeUInt32BE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式(Big-Endian:大字節序)將 value 寫入buffer。注意:value 必須是一個合法的有符號 32 位整數。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者offset可能會超出buffer的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
40 buf.writeInt8(value, offset[, noAssert])<br根據傳入的 offset="" 偏移量將="" value="" 寫入="" buffer="" 。注意:value="" 必須是一個合法的="" signed="" 8="" 位整數。="" 若參數="" noassert="" 爲="" true="" 將不會驗證="" 和="" 偏移量參數。="" 這意味着="" 可能過大,或者="" 可能會超出="" 的末尾從而形成="" 被丟棄。="" 除非你對這個參數很是有把握,不然儘可能不要使用。默認是="" false。<="" td="">
41 buf.writeInt16LE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的 signed 16 位整數。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false 。
42 buf.writeInt16BE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的 signed 16 位整數。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false 。
43 buf.writeInt32LE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的 signed 32 位整數。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
44 buf.writeInt32BE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個合法的 signed 32 位整數。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
45 buf.writeFloatLE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer 。注意:當 value 不是一個 32 位浮點數類型的值時,結果將是不肯定的。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value可能過大,或者 offset 可能會超出 buffer 的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
46 buf.writeFloatBE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer 。注意:當 value 不是一個 32 位浮點數類型的值時,結果將是不肯定的。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value可能過大,或者 offset 可能會超出 buffer 的末尾從而形成 value 被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
47 buf.writeDoubleLE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個有效的 64 位double 類型的值。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而形成value被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
48 buf.writeDoubleBE(value, offset[, noAssert])
根據傳入的 offset 偏移量和指定的 endian 格式將 value 寫入 buffer。注意:value 必須是一個有效的 64 位double 類型的值。 若參數 noAssert 爲 true 將不會驗證 value 和 offset 偏移量參數。 這意味着 value 可能過大,或者 offset 可能會超出 buffer 的末尾從而形成value被丟棄。 除非你對這個參數很是有把握,不然儘可能不要使用。默認是 false。
49 buf.fill(value[, offset][, end])
使用指定的 value 來填充這個 buffer。若是沒有指定 offset (默認是 0) 而且 end (默認是 buffer.length) ,將會填充整個buffer。

Node.js Stream(流)

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

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

  • Readable - 可讀操做。

  • Writable - 可寫操做。

  • Duplex - 可讀可寫操做.

  • Transform - 操做被寫入數據,而後讀出結果。

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

  • data - 當有數據可讀時觸發。

  • end - 沒有更多的數據可讀時觸發。

  • error - 在接收和寫入過程當中發生錯誤時觸發。

  • finish - 全部數據已被寫入到底層系統時觸發。

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

從流中讀取數據

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

mikey 

建立 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("程序執行完畢");
View Code

 

以上代碼執行結果以下:

程序執行完畢 mikey

寫入流

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

var fs = require("fs");
var data = 'mikey';

// 建立一個能夠寫入的流,寫入到文件 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("程序執行完畢");
View Code

 

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

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

查看 output.txt 文件的內容:

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

管道流

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

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

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

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

mikey 管道流操做實例

建立 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 
mikey
管道流操做實例

 

鏈式流

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

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

建立 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 
文件解壓完成。

Node.js模塊系統

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

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

建立模塊

在 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。

服務端的模塊放在哪裏

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

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
View Code

 

exports 和 module.exports 的使用

若是要對外暴露屬性或方法,就用 exports 就行,要暴露對象(相似class,包含了不少屬性和方法),就用 module.exports

 

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 時能夠傳遞這樣一個變量。

匿名函數

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

function execute(someFunction, value) {
  someFunction(value);
}

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

 

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

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

函數傳遞是如何讓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);

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"]
View Code

 

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

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

server.js 文件代碼:

var http = require('http');
var url = require('url');

function start(router) {
    function onRequest(request, response) {
        var pathname = url.parse(request.url).pathname;
        console.log('請求路徑:'+pathname);
        router(pathname);
        response.writeHead(200, {"Content-Type":"text/plain"});
        response.write("hello world");
        response.end();
    }
    http.createServer(onRequest).listen(8888);
    console.log('服務已經啓動');
}
exports.start = start;
Server.js

 

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

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

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

router.js 文件代碼:

function router(pathname) {
    console.log("about to route a request for " + pathname);
}

exports.router = router;
Router.js

 

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

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

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

server.js 文件代碼:

var http = require('http');
var url = require('url');

function start(router) {
    function onRequest(request, response) {
        var pathname = url.parse(request.url).pathname;
        console.log('請求路徑:'+pathname);
        router(pathname);
        response.writeHead(200, {"Content-Type":"text/plain"});
        response.write("hello world");
        response.end();
    }
    http.createServer(onRequest).listen(8888);
    console.log('服務已經啓動');
}
exports.start = start;
Server.js

 

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

index.js 文件代碼:

var server = require('./Server');
var router = require('./router');

server.start(router.router);

 

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

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

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

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

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 文件,結果以下所示:

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 文件,代碼以下所示:

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

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);
View Code

 

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

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

 

注意: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)
View Code

 

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));
View Code

 

運行結果是:

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

 

util.isArray(object)

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

var util = require('util');

util.isArray([])
  // true
util.isArray(new Array)
  // true
util.isArray({})
  // false
View Code

 

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
View Code

 

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
View Code

 

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
View Code

Node.js 文件系統

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

var fs = require("fs")

 

異步和同步

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("程序執行完畢。");
View Code

 

以上代碼執行結果以下:

$ node file.js 
同步讀取: 菜鳥教程官網地址:www.runoob.com
文件讀取實例

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

 

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

打開文件

語法

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

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("文件打開成功!");     
});
View Code

 

以上代碼執行結果以下:

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

 

獲取文件信息

語法

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

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

 

寫入文件

語法

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

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());
   });
});
View Code

 

以上代碼執行結果以下:

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

 

讀取文件

語法

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

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.alloc(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());
      }
   });
});
View Code

 

以上代碼執行結果以下:

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

 

關閉文件

語法

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

fs.close(fd, callback)

 

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

參數

參數使用說明以下:

  • fd - 經過 fs.open() 方法返回的文件描述符。

  • callback - 回調函數,沒有參數。

實例

input.txt 文件內容爲:

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

 

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

var fs = require("fs");
var buf = new Buffer.alloc(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("文件關閉成功");
      });
   });
});
View Code

 

以上代碼執行結果以下:

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

 

截取文件

語法

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

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.alloc(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("文件關閉成功!");
         });
      });
   });
});
View Code

 

以上代碼執行結果以下:

$ node file.js 
準備打開文件!
文件打開成功!
截取10字節內的文件內容,超出部分將被去除。
文件截取成功。
讀取相同的文件
site:www.r
文件關閉成功

 

刪除文件

語法

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

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 文件,發現已經不存在了。

建立目錄

語法

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

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

 

參數

參數使用說明以下:

  • path - 文件路徑。

  • options 參數能夠是:

    • recursive - 是否以遞歸的方式建立目錄,默認爲 false。
    • mode - 設置目錄權限,默認爲 0777。
  • callback - 回調函數,沒有參數。

實例

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

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

 

以上代碼執行結果以下:

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

 

能夠添加 recursive: true 參數,無論建立的目錄 /tmp 和 /tmp/a 是否存在:

fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
  if (err) throw err;
});

 

讀取目錄

語法

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

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

 

刪除目錄

語法

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

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 );
      });
   });
});
View Code

 

以上代碼執行結果以下:

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

 

文件模塊方法參考手冊

如下爲 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).回調函數沒有參數,但可能拋出異常。 訪問權限默認爲 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])<br同步版的 fs.readfile.<="" 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().回調函數沒有參數,但可能拋出異常。

更多內容,請查看官網文件模塊描述:File System

 

Node.js GET/POST請求

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

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

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

獲取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);
View Code

 

在瀏覽器中訪問 http://127.0.0.1:3000/user?name=mikey&url=test 而後查看返回結果:

 

 

獲取 POST 請求內容

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

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

基本語法結構說明

var http = require('http');
var querystring = require('querystring');
var util = require('util');
 
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);
View Code

如下實例表單經過 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);
View Code

 

Node.js 工具模塊

在 Node.js 模塊庫中有不少好用的模塊。接下來咱們爲你們介紹幾種經常使用模塊的使用:

序號 模塊名 & 描述
1 OS 模塊
提供基本的系統操做函數。
2 Path 模塊
提供了處理和轉換文件路徑的工具。
3 Net 模塊
用於底層的網絡通訊。提供了服務端和客戶端的的操做。
4 DNS 模塊
用於解析域名。
5 Domain 模塊
簡化異步代碼的異常處理,能夠捕捉處理try catch沒法捕捉的。

 

Node.js Web 模塊

什麼是 Web 服務器?

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

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

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

Web 應用架構

  • Client - 客戶端,通常指瀏覽器,瀏覽器能夠經過 HTTP 協議向服務器請求數據。

  • Server - 服務端,通常指 Web 服務器,能夠接收客戶端請求,並向客戶端發送響應數據。

  • Business - 業務層, 經過 Web 服務器處理應用程序,如與數據庫交互,邏輯運算,調用外部程序等。

  • Data - 數據層,通常由數據庫組成。

使用 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 (req, res) {
    // 解析請求
    var pathname = url.parse(req.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/html
            res.writeHead(404,{'Content-Type':'text/html'});
        }else {
            // HTTP 狀態碼:200 :OK
            // Content Type : text/html
            res.writeHead(200,{'Content-Type':'text/html'});
            res.write(data.toString());
        }
        res.end();
    });
}).listen(8080);

console.log('Server running at http://127.0.0.1:8080/');
View Code

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

index.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index page</title>
</head>
<body>
<h1>Index page</h1>
<p>this is p lable</p>
</body>
</html>
View Code

執行 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.     #  客戶端請求信息

使用 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();
View Code

新開一個終端,執行 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>
View Code

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

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

Node.js Express 框架

Express 簡介

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

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

Express 框架核心特性:

  • 能夠設置中間件來響應 HTTP 請求。

  • 定義了路由表用於執行不一樣的 HTTP 請求動做。

  • 能夠經過向模板傳遞參數來動態渲染 HTML 頁面。

安裝 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

 

第一個 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)
 
})
View Code

執行以上代碼:

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

 

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

請求和響應

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

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

 

request 和 response 對象的具體介紹:

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

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

 

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

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

 

路由

咱們已經瞭解了 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)
 
})
View Code

 

執行以上代碼:
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

靜態文件

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

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

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

 

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

node_modules
server.js public/ public/images public/images/logo.png

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

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

express_demo3.js 文件代碼:

var express = require('express');
var app = express();
 
app.use('/public', 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)
 
})
View Code

執行以上代碼:

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

 

執行以上代碼:

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

GET 方法

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

index.htm 文件代碼:

<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>
View Code

 

server.js 文件代碼:

var express = require('express');
var app = express();
 
app.use('/public', 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)
 
})
View Code

 

執行以上代碼:

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

 

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

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


POST 方法

如下實例演示了在表單中經過 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>
View Code

 

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('/public', 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)
 
})
View Code

執行以上代碼:

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

 

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

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


文件上傳

如下咱們建立一個用於上傳文件的表單,使用 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>
View Code

 

server.js 文件代碼:

var express = require('express');
var app = express();
var fs = require("fs");
 
var bodyParser = require('body-parser');
var multer  = require('multer');
 
app.use('/public', 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)
 
})
View Code

 

執行以上代碼:

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

 

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

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


Cookie 管理

咱們可使用中間件向 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)
View Code

 

執行以上代碼:

node express_cookie.js 

 

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


相關資料

Node.js RESTful API

什麼是 REST?

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

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

HTTP 方法

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

 

  • GET - 用於獲取數據。

  • PUT - 用於更新或添加數據。

  • DELETE - 用於刪除數據。

  • POST - 用於添加數據。

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 架構詳解

建立 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
   }
}
View Code

 

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

序號 URI HTTP 方法 發送內容 結果
1 listUsers GET 顯示全部用戶列表
2 addUser POST JSON 字符串 添加新用戶
3 deleteUser DELETE JSON 字符串 刪除用戶
4 :id GET 顯示用戶詳細信息

獲取用戶列表:

如下代碼,咱們建立了 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)

})
View Code

 

接下來執行如下命令:

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
   }
}
View Code

 

添加用戶

如下代碼,咱們建立了 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)
})
View Code

 

接下來執行如下命令:

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 } 
}
View Code

 

顯示用戶詳情

如下代碼,咱們建立了 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)

})
View Code

 

接下來執行如下命令:

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

 

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

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

 

刪除用戶

如下代碼,咱們建立了 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" + id];
       
       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)

})
View Code

 

接下來執行如下命令:

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 } 
}
View Code

 

Node.js 多進程

咱們都知道 Node.js 是以單線程的模式運行的,但它使用的是事件驅動來處理併發,這樣有助於咱們在多核 cpu 的系統上建立多個子進程,從而提升性能。

每一個子進程老是帶有三個流對象:

child.stdin

child.stdout

child.stderr

他們可能會共享父進程的 stdio 流,或者也能夠是獨立的被導流的流對象。

Node 提供了 child_process 模塊來建立子進程,方法有:

  • exec - child_process.exec 使用子進程執行命令,緩存子進程的輸出,並將子進程的輸出以回調函數參數的形式返回。

  • spawn - child_process.spawn 使用指定的命令行參數建立新進程。

  • fork - child_process.fork 是 spawn()的特殊形式,用於在子進程中運行的模塊,如 fork('./son.js') 至關於 spawn('node', ['./son.js']) 。與spawn方法不一樣的是,fork會在父進程與子進程之間,創建一個通訊管道,用於進程之間的通訊。

exec() 方法

child_process.exec 使用子進程執行命令,緩存子進程的輸出,並將子進程的輸出以回調函數參數的形式返回。

語法以下所示:

child_process.exec(command[, options], callback)

 

參數

參數說明以下:

command: 字符串, 將要運行的命令,參數使用空格隔開

options :對象,能夠是:

  • cwd ,字符串,子進程的當前工做目錄
  • env,對象 環境變量鍵值對
  • encoding ,字符串,字符編碼(默認: 'utf8')
  • shell ,字符串,將要執行命令的 Shell(默認: 在 UNIX 中爲/bin/sh, 在 Windows 中爲cmd.exe, Shell 應當能識別 -c開關在 UNIX 中,或 /s /c 在 Windows 中。 在Windows 中,命令行解析應當能兼容cmd.exe
  • timeout,數字,超時時間(默認: 0)
  • maxBuffer,數字, 在 stdout 或 stderr 中容許存在的最大緩衝(二進制),若是超出那麼子進程將會被殺死 (默認: 200*1024)
  • killSignal ,字符串,結束信號(默認:'SIGTERM')
  • uid,數字,設置用戶進程的 ID
  • gid,數字,設置進程組的 ID

callback :回調函數,包含三個參數error, stdout 和 stderr。

exec() 方法返回最大的緩衝區,並等待進程結束,一次性返回緩衝區的內容。

實例

讓咱們建立兩個 js 文件 support.js 和 master.js。

support.js 文件代碼:

console.log("進程 " + process.argv[2] + " 執行。" );

master.js 文件代碼:

const fs = require('fs'); const child_process = require('child_process'); for(var i=0; i<3; i++) { var workerProcess = child_process.exec('node support.js '+i, function (error, stdout, stderr) { if (error) { console.log(error.stack); console.log('Error code: '+error.code); console.log('Signal received: '+error.signal); } console.log('stdout: ' + stdout); console.log('stderr: ' + stderr); }); workerProcess.on('exit', function (code) { console.log('子進程已退出,退出碼 '+code); }); }

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

node master.js 
子進程已退出,退出碼 0
stdout: 進程 1 執行。

stderr: 
子進程已退出,退出碼 0
stdout: 進程 0 執行。

stderr: 
子進程已退出,退出碼 0
stdout: 進程 2 執行。

stderr:

 

spawn() 方法

child_process.spawn 使用指定的命令行參數建立新進程,語法格式以下:

child_process.spawn(command[, args][, options])

 

參數

參數說明以下:

command: 將要運行的命令

args: Array 字符串參數數組

options Object

  • cwd String 子進程的當前工做目錄
  • env Object 環境變量鍵值對
  • stdio Array|String 子進程的 stdio 配置
  • detached Boolean 這個子進程將會變成進程組的領導
  • uid Number 設置用戶進程的 ID
  • gid Number 設置進程組的 ID

spawn() 方法返回流 (stdout & stderr),在進程返回大量數據時使用。進程一旦開始執行時 spawn() 就開始接收響應。

實例

讓咱們建立兩個 js 文件 support.js 和 master.js。

support.js 文件代碼:

console.log("進程 " + process.argv[2] + " 執行。" );

master.js 文件代碼:

const fs = require('fs');
const child_process = require('child_process');
 
for(var i=0; i<3; i++) {
   var workerProcess = child_process.spawn('node', ['support.js', i]);
 
   workerProcess.stdout.on('data', function (data) {
      console.log('stdout: ' + data);
   });
 
   workerProcess.stderr.on('data', function (data) {
      console.log('stderr: ' + data);
   });
 
   workerProcess.on('close', function (code) {
      console.log('子進程已退出,退出碼 '+code);
   });
}
View Code

 

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

node master.js stdout: 進程 0 執行。
子進程已退出,退出碼 0
stdout: 進程 1 執行。
子進程已退出,退出碼 0
stdout: 進程 2 執行。
子進程已退出,退出碼 0

 

fork 方法

child_process.fork 是 spawn() 方法的特殊形式,用於建立進程,語法格式以下:

child_process.fork(modulePath[, args][, options])

 

參數

參數說明以下:

modulePath: String,將要在子進程中運行的模塊

args: Array 字符串參數數組

options:Object

  • cwd String 子進程的當前工做目錄
  • env Object 環境變量鍵值對
  • execPath String 建立子進程的可執行文件
  • execArgv Array 子進程的可執行文件的字符串參數數組(默認: process.execArgv)
  • silent Boolean 若是爲true,子進程的stdinstdoutstderr將會被關聯至父進程,不然,它們將會從父進程中繼承。(默認爲:false
  • uid Number 設置用戶進程的 ID
  • gid Number 設置進程組的 ID

返回的對象除了擁有ChildProcess實例的全部方法,還有一個內建的通訊信道。

實例

讓咱們建立兩個 js 文件 support.js 和 master.js。

support.js 文件代碼:

console.log("進程 " + process.argv[2] + " 執行。" );

master.js 文件代碼:

const fs = require('fs');
const child_process = require('child_process');
 
for(var i=0; i<3; i++) {
   var worker_process = child_process.fork("support.js", [i]);    
 
   worker_process.on('close', function (code) {
      console.log('子進程已退出,退出碼 ' + code);
   });
}
View Code

 

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

node master.js 
進程 0 執行。
子進程已退出,退出碼 0
進程 1 執行。
子進程已退出,退出碼 0
進程 2 執行。
子進程已退出,退出碼 0

Node.js JXcore 打包

Node.js 是一個開放源代碼、跨平臺的、用於服務器端和網絡應用的運行環境。

JXcore 是一個支持多線程的 Node.js 發行版本,基本不須要對你現有的代碼作任何改動就能夠直接線程安全地以多線程運行。

這篇文章主要是要向你們介紹 JXcore 的打包功能。

JXcore 安裝

下載 JXcore 安裝包,並解壓,在解壓的的目錄下提供了 jx 二進制文件命令,接下來咱們主要使用這個命令。

步驟一、下載

下載 JXcore 安裝包 https://github.com/jxcore/jxcore-release,你須要根據你本身的系統環境來下載安裝包。

一、Window 平臺下載:Download(Windows x64 (V8))

二、Linux/OSX 安裝命令:

curl https://raw.githubusercontent.com/jxcore/jxcore/master/tools/jx_install.sh | bash

 

若是權限不足,可使用如下命令:

curl https://raw.githubusercontent.com/jxcore/jxcore/master/tools/jx_install.sh | sudo bash

 

以上步驟若是操做正確,使用如下命令,會輸出版本號信息:

jx --version
v0.10.32

包代碼

例如,咱們的 Node.js 項目包含如下幾個文件,其中 index.js 是主文件:

drwxr-xr-x  2 root root  4096 Nov 13 12:42 images
-rwxr-xr-x  1 root root 30457 Mar  6 12:19 index.htm
-rwxr-xr-x  1 root root 30452 Mar  1 12:54 index.js
drwxr-xr-x 23 root root  4096 Jan 15 03:48 node_modules
drwxr-xr-x  2 root root  4096 Mar 21 06:10 scripts
drwxr-xr-x  2 root root  4096 Feb 15 11:56 style

 

接下來咱們使用 jx 命令打包以上項目,並指定 index.js 爲 Node.js 項目的主文件:

 jx package index.js index

 

以上命令執行成功,會生成如下兩個文件:

  • index.jxp 這是一箇中間件文件,包含了須要編譯的完整項目信息。

  • index.jx 這是一個完整包信息的二進制文件,可運行在客戶端上。

載入 JX 文件

Node.js 的項目運行:

node index.js command_line_arguments

 

使用 JXcore 編譯後,咱們可使用如下命令來執行生成的 jx 二進制文件:

jx index.jx command_line_arguments

 

更多 JXcore 安裝參考:https://github.com/jxcore/jxcore/blob/master/doc/INSTALLATION.md

更多 JXcore 功能特性你能夠參考官網:https://github.com/jxcore/jxcore

Node.js 鏈接 MySQL

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

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

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

安裝驅動

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

cnpm install mysql

 

鏈接數據庫

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

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);
});
View Code

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

node test.js
The solution is: 2

數據庫鏈接參數說明:

參數 描述
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


數據庫操做( CURD )

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

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

查詢數據

將上面咱們提供的 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();
View Code

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

--------------------------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' } ]
------------------------------------------------------------
View Code

 

插入數據

咱們能夠向數據表 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();
View Code

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

--------------------------INSERT----------------------------
INSERT ID: OkPacket {
  fieldCount: 0,
  affectedRows: 1,
  insertId: 6,
  serverStatus: 2,
  warningCount: 0,
  message: '',
  protocol41: true,
  changedRows: 0 }
-----------------------------------------------------------------
View Code

 

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

更新數據

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

更新數據

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();
View Code

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

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

 

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

刪除數據

咱們可使用如下代碼來刪除 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();
View Code

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

--------------------------DELETE----------------------------
DELETE affectedRows 1
-----------------------------------------------------------------

 

執行成功後,查看數據表,便可以看到 id=6 的數據已被刪除:

 

Node.js 鏈接 MongoDB

MongoDB是一種文檔導向數據庫管理系統,由C++撰寫而成。

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

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

安裝驅動

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

cnpm install mongodb

接下來咱們來實現增刪改查功能。

建立數據庫

要在 MongoDB 中建立一個數據庫,首先咱們須要建立一個 MongoClient 對象,而後配置好指定的 URL 和 端口號。

若是數據庫不存在,MongoDB 將建立數據庫並創建鏈接。

建立鏈接

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/runoob";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
  if (err) throw err;
  console.log("數據庫已建立!");
  db.close();
});
View Code

建立集合

咱們可使用 createCollection() 方法來建立集合:

建立集合

var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/runoob';
MongoClient.connect(url, { useNewUrlParser: true }, function (err, db) {
    if (err) throw err;
    console.log('數據庫已建立');
    var dbase = db.db("runoob");
    dbase.createCollection('site', function (err, res) {
        if (err) throw err;
        console.log("建立集合!");
        db.close();
    });
});
View Code

數據庫操做( CURD )

與 MySQL 不一樣的是 MongoDB 會自動建立數據庫和集合,因此使用前咱們不須要手動去建立。

插入數據

如下實例咱們鏈接數據庫 runoob 的 site 表,並插入一條數據條數據,使用 insertOne():

插入一條數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    var myobj = { name: "菜鳥教程", url: "www.runoob" };
    dbo.collection("site").insertOne(myobj, function(err, res) {
        if (err) throw err;
        console.log("文檔插入成功");
        db.close();
    });
});
View Code

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

node test.js
文檔插入成功

從輸出結果來看,數據已插入成功。

咱們也能夠打開 MongoDB 的客戶端查看數據,如:

> show dbs
runoob  0.000GB          # 自動建立了 runoob 數據庫
> show tables
site                     # 自動建立了 site 集合(數據表)
> db.site.find()
{ "_id" : ObjectId("5a794e36763eb821b24db854"), "name" : "菜鳥教程", "url" : "www.runoob" }
>
View Code

若是要插入多條數據可使用 insertMany():

插入多條數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    var myobj =  [
        { name: '菜鳥工具', url: 'https://c.runoob.com', type: 'cn'},
        { name: 'Google', url: 'https://www.google.com', type: 'en'},
        { name: 'Facebook', url: 'https://www.google.com', type: 'en'}
       ];
    dbo.collection("site").insertMany(myobj, function(err, res) {
        if (err) throw err;
        console.log("插入的文檔數量爲: " + res.insertedCount);
        db.close();
    });
});
View Code

查詢數據

可使用 find() 來查找數據, find() 能夠返回匹配條件的全部數據。 若是未指定條件,find() 返回集合中的全部數據。

find()

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    dbo.collection("site"). find({}).toArray(function(err, result) { // 返回集合中全部數據
        if (err) throw err;
        console.log(result);
        db.close();
    });
});
View Code

如下實例檢索 name 爲 "菜鳥教程" 的實例:

查詢指定條件的數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
     var whereStr = {"name":'菜鳥教程'};  // 查詢條件
    dbo.collection("site").find(whereStr).toArray(function(err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});
View Code
執行如下命令輸出就結果爲:
[ { _id: 5a794e36763eb821b24db854,
    name: '菜鳥教程',
    url: 'www.runoob' } ]

更新數據

咱們也能夠對數據庫的數據進行修改,如下實例將 name 爲 "菜鳥教程" 的 url 改成 https://www.runoob.com:

更新一條數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    var whereStr = {"name":'菜鳥教程'};  // 查詢條件
    var updateStr = {$set: { "url" : "https://www.runoob.com" }};
    dbo.collection("site").updateOne(whereStr, updateStr, function(err, res) {
        if (err) throw err;
        console.log("文檔更新成功");
        db.close();
    });
});
View Code

執行成功後,進入 mongo 管理工具查看數據已修改:

> db.site.find().pretty()
{
    "_id" : ObjectId("5a794e36763eb821b24db854"),
    "name" : "菜鳥教程",
    "url" : "https://www.runoob.com"     // 已修改成 https
}

 

若是要更新全部符合條的文檔數據可使用 updateMany():

更新多條數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    var whereStr = {"type":'en'};  // 查詢條件
    var updateStr = {$set: { "url" : "https://www.runoob.com" }};
    dbo.collection("site").updateMany(whereStr, updateStr, function(err, res) {
        if (err) throw err;
         console.log(res.result.nModified + " 條文檔被更新");
        db.close();
    });
});
View Code

result.nModified 爲更新的條數。

刪除數據

如下實例將 name 爲 "菜鳥教程" 的數據刪除 :

刪除一條數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    var whereStr = {"name":'菜鳥教程'};  // 查詢條件
    dbo.collection("site").deleteOne(whereStr, function(err, obj) {
        if (err) throw err;
        console.log("文檔刪除成功");
        db.close();
    });
});
View Code
執行成功後,進入 mongo 管理工具查看數據已刪除:
> db.site.find()
>

若是要刪除多條語句可使用 deleteMany() 方法

如下實例將 type 爲 en 的全部數據刪除 :

刪除多條數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    var whereStr = { type: "en" };  // 查詢條件
    dbo.collection("site").deleteMany(whereStr, function(err, obj) {
        if (err) throw err;
        console.log(obj.result.n + " 條文檔被刪除");
        db.close();
    });
});
View Code

obj.result.n 刪除的條數。

排序

排序 使用 sort() 方法,該方法接受一個參數,規定是升序(1)仍是降序(-1)。

例如:

{ type: 1 }  // 按 type 字段升序
{ type: -1 } // 按 type 字段降序

按 type 升序排列:

排序

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    var mysort = { type: 1 };
    dbo.collection("site").find().sort(mysort).toArray(function(err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});
View Code

查詢分頁

若是要設置指定的返回條數可使用 limit() 方法,該方法只接受一個參數,指定了返回的條數。

limit():讀取兩條數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    dbo.collection("site").find().limit(2).toArray(function(err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
  });
});
View Code

若是要指定跳過的條數,可使用 skip() 方法。

skip(): 跳過前面兩條數據,讀取兩條數據

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    dbo.collection("site").find().skip(2).limit(2).toArray(function(err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
  });
});
View Code

鏈接操做

mongoDB 不是一個關係型數據庫,但咱們可使用 $lookup 來實現左鏈接。

例如咱們有兩個集合數據分別爲:

集合1:orders

[ { _id: 1, product_id: 154, status: 1 } ]

集合2:products

[ { _id: 154, name: '筆記本電腦' }, { _id: 155, name: '耳機' }, { _id: 156, name: '臺式電腦' } ]

$lookup 實現左鏈接

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
  if (err) throw err;
  var dbo = db.db("runoob");
  dbo.collection('orders').aggregate([
    { $lookup:
       {
         from: 'products',            // 右集合
         localField: 'product_id',    // 左集合 join 字段
         foreignField: '_id',         // 右集合 join 字段
         as: 'orderdetails'           // 新生成字段(類型array)
       }
     }
    ]).toArray(function(err, res) {
    if (err) throw err;
    console.log(JSON.stringify(res));
    db.close();
  });
});
View Code

刪除集合

咱們可使用 drop() 方法來刪除集合:

drop()

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
 
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
    if (err) throw err;
    var dbo = db.db("runoob");
    // 刪除 test 集合
    dbo.collection("test").drop(function(err, delOK) {  // 執行成功 delOK 返回 true,不然返回 false
        if (err) throw err;
        if (delOK) console.log("集合已刪除");
        db.close();
    });
});
View Code
相關文章
相關標籤/搜索