简介
简单的说 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
若不在环境变量中可添加软链接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 应用是由哪几部分组成的:
-
引入 required 模块:咱们可使用 require 指令来载入 Node.js 模块。
-
建立服务器:服务器能够监听客户端的请求,相似于 Apache 、Nginx 等 HTTP 服务器。
-
接收请求与响应请求 服务器很容易建立,客户端可使用浏览器或终端发送 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安装:
一、安装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:
安装好以后,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
安装过程输出以下内容,第一行输出了模块的版本号及安装位置。
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
查看安装信息
你可使用如下命令来查看全部全局安装的模块:
├─┬ 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
若是要查看某个模块的版本号,可使用命令以下:
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 模块。
卸载后,你能够到 /node_modules/ 目录下查看包是否还存在,或者使用如下命令查看:
更新模块
咱们可使用如下命令更新模块:
搜索模块
使用如下来搜索模块:
建立模块
建立模块,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 资源库中注册用户(使用邮箱注册):
Username: mcmohd Password: Email: (this IS public) mcmohd@gmail.com
接下来咱们就用如下命令来发布模块:
若是你以上的步骤都操做正确,你就能够跟其余模块同样使用 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提供了不少命令,例如install
和publish
,使用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 命令来安装模块了:
更多信息能够查阅:http://npm.taobao.org/。
npm cache clean
npm install
Node.js REPL(交互式解释器)
Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,相似 Window 系统的终端或 Unix/Linux shell,咱们能够在终端中输入命令,并接收系统的响应。
Node 自带了交互式解释器,能够执行如下任务:
Node 的交互式解释器能够很好的调试 Javascript 代码。
开始学习 REPL
咱们能够输入如下命令来启动 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 ,内容以下:
建立 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 和 emit。on 函数用于绑定事件函数,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 缓冲区数据的语法以下所示:
buf.toString([encoding[, start[, 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 对象的函数语法格式以下:
当字符串化一个 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])
参数
参数描述以下:
返回值
返回一个多个成员合并的新 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);
参数
参数描述以下:
返回值
返回一个数字,表示 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 + "以后");
}
执行以上代码,输出结果为:
拷贝缓冲区
语法
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());
执行以上代码,输出结果为:
缓冲区裁剪
Node 缓冲区裁剪语法以下所示:
buf.slice([start[, end]])
参数
参数描述以下:
返回值
返回一个新的缓冲区,它和旧缓冲区指向同一块内存,可是从索引 start 到 end 的位置剪切。
实例
var buffer1 = Buffer.from('runoob');
// 剪切缓冲区
var buffer2 = buffer1.slice(0,2);
console.log("buffer2 content: " + buffer2.toString());
执行以上代码,输出结果为:
缓冲区长度
语法
Node 缓冲区长度计算语法以下所示:
返回值
返回 Buffer 对象所占据的内存长度。
实例
var buffer = Buffer.from('www.runoob.com');
// 缓冲区长度
console.log("buffer length: " + buffer.length);
执行以上代码,输出结果为:
方法参考手册
如下列出了 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 有四种流类型:
全部的 Stream 对象都是 EventEmitter 的实例。经常使用的事件有:
本教程会为你们介绍经常使用的流操做。
从流中读取数据
建立 input.txt 文件,内容以下:
建立 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 文件中。代码执行结果以下:
程序执行完毕 写入完成。
查看 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("程序执行完毕");
代码执行结果以下:
程序执行完毕
查看 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 文件,代码以下所示:
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 #<Sub> has no method 'sayHello'
at Object.<anonymous> (/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)语法以下所示:
异步和同步
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)
参数
参数使用说明以下:
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)执行后,会将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 文件内容为:
接下来咱们建立 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
关闭文件
语法
如下为异步模式下关闭文件的语法格式:
该方法使用了文件描述符来读取文件。
参数
参数使用说明以下:
实例
input.txt 文件内容为:
接下来咱们建立 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)
该方法使用了文件描述符来读取文件。
参数
参数使用说明以下:
实例
input.txt 文件内容为:
接下来咱们建立 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 文件内容为:
接下来咱们建立 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)
参数
参数使用说明以下:
实例
接下来咱们建立 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
删除目录
语法
如下为删除目录的语法格式:
参数
参数使用说明以下:
-
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 模块库中有不少好用的模块。接下来咱们为你们介绍几种经常使用模块的使用:
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 框架核心特性:
安装 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
执行以上代码:
如今你能够访问 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
,子进程的stdin
,stdout
和stderr
将会被关联至父进程,不然,它们将会从父进程中继承。(默认为: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
以上步骤若是操做正确,使用如下命令,会输出版本号信息:
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
以上命令执行成功,会生成如下两个文件:
载入 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 命令进行安装:
链接数据库
在如下实例中根据你的实际配置修改数据库用户名、及密码及数据库名:
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 命令进行安装:
接下来咱们来实现增删改查功能。
建立数据库
要在 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
执行如下命令输出就结果为:
从输出结果来看,数据已插入成功。
咱们也能够打开 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 管理工具查看数据已删除:
若是要删除多条语句可使用 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
若是你遇到了使用 npm 安 装node_modules 老是提示报错:报错: npm resource busy or locked.....。
能够先删除之前安装的 node_modules :