ZhangYang's Blog

Node.js基础

简介

Node.js 就是运行在服务端的 JavaScript,是一个平台,提供I/O接口,基于Google的V8引擎

创建第一个应用

脚本模式

保存该文件,文件名为 helloworld.js

1
console.log("Hello World");

并通过 node命令来执行,输出Hello World!

1
node helloworld.js

交货模式

打开终端,键入node进入命令交互模式,可以输入一条代码语句后立即执行并显示结果

1
2
3
$ node
> console.log('Hello World!');
Hello World!

NPM 使用介绍

NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景

  • 允许用户从NPM服务器下载别人编写的第三方包到本地使用
  • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用
  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用

使用 npm 命令安装模块

使用 npm 命令安装常用的 Node.js web框架模块 express

1
npm install express

安装好之后,express 包就放在了工程目录下的 node_modules 目录中

因此在代码中只需要通过 require(‘express’) 的方式就好,无需指定第三方包路径

1
var express = require('express');

全局安装与本地安装

npm 的包安装分为本地安装、全局安装

1
2
npm install express # 本地安装
npm install express -g # 全局安装

本地安装

  • 将安装包放在 ./node_modules 下,如果没有 node_modules 目录
  • 可以通过 require() 来引入本地安装的包

全局安装

  • 将安装包放在 /usr/local 下或者你 node 的安装目录。
  • 可以直接在命令行里使用

使用 package.json

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 模块

1
npm uninstall express

更新模块

使用以下命令更新模块

1
npm update express

搜索模块

使用以下来搜索模块

1
npm search express

创建模块

创建模块,package.json 文件是必不可少的

1
npm init

使用以下命令在 npm 资源库中注册用户(使用邮箱注册)

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

用以下命令来发布模块

1
npm publish

NPM 常用命令

NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令

  • 使用npm help 可查看某条命令的详细帮助,例如npm help install
  • 在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试
  • 使用npm update 可以把当前目录下node_modules子目录里边的对应模块更新至最新版本
  • 使用npm update -g可以把全局安装的对应命令行程序更新至最新版
  • 使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人
  • 使用npm unpublish @可以撤销发布自己发布过的某个版本代码

使用淘宝 NPM 镜像

1
2
3
4
5
// 使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm
npm install -g cnpm --registry=https://registry.npm.taobao.org
// 使用 cnpm 命令来安装模块
// cnpm install [name]

REPL(交互式解释器)

Node 自带了交互式解释器,可以执行以下任务

  • 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
  • 执行 - 执行输入的数据结构
  • 打印 - 输出结果
  • 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出

输入以下命令来启动 Node 的终端

1
2
node
>

简单的表达式运算

1
2
3
4
5
6
7
8
9
10
11
12
$ node
> 1 +4
5
> 5 / 2
2.5
> 3 * 6
18
> 4 - 1
3
> 1 + ( 2 * 3 ) - 4
3
>

使用变量

将数据存储在变量中,并在你需要的时候使用它

1
2
3
4
5
6
7
8
9
10
11
12
13
$ 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 三个点的符号是系统自动生成的,你回车换行后即可。Node 会自动检测是否为连续的表达式
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
>

下划线(_)变量

使用下划线(_)获取上一个表达式的运算结果

1
2
3
4
5
6
7
8
9
10
11
12
13
$ 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

1
2
3
4
$ node
>
(^C again to quit)
>

回调函数

Node.js 异步编程的直接体现就是回调

回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数

一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。执行代码时就没有阻塞或等待文件 I/O 操作

阻塞代码实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建一个文件 input.txt
张扬博客地址:https://github.com/zy343134464
// 创建 main.js 文件
var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执行结束!");
// 输出结果
// node main.js
张扬博客地址:https://github.com/zy343134464
程序执行结束!

非阻塞代码实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建一个文件 input.txt
张扬博客地址:https://github.com/zy343134464
// 创建 main.js 文件
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执行结束!");
// 输出结果
// node main.js
程序执行结束!
张扬博客地址:https://github.com/zy343134464

事件循环

Node.js 是单进程单线程应用程序,V8 引擎提供的异步执行回调接口

Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现

Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数

事件驱动程序

Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求

当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户

在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数

image

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件

1
2
3
4
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

绑定及触发事件处理程序

1
2
3
4
5
// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);
// 触发事件
eventEmitter.emit('eventName');

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 创建main.js
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
// 创建事件处理程序
var connectHandler = function connected() {
console.log('连接成功。');
// 触发 data_received 事件
eventEmitter.emit('data_received');
}
// 绑定 connection 事件处理程序
eventEmitter.on('connection', connectHandler);
// 使用匿名函数绑定 data_received 事件
eventEmitter.on('data_received', function(){
console.log('数据接收成功。');
});
// 触发 connection 事件
eventEmitter.emit('connection');
console.log("程序执行完毕。");
// 输出
// node main.js
连接成功。
数据接收成功。
程序执行完毕。

应用程序工作机制

在 Node 应用程序中,回调函数接收错误对象作为第一个参数,执行异步操作的函数将回调函数作为最后一个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// fs.readFile() 是异步函数用于读取文件
// 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息
// 果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出
// 创建一个 input.txt
张扬博客地址:https://github.com/zy343134464
// 创建 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("程序执行完毕");
// 输出
程序执行完毕
张扬博客地址:https://github.com/zy343134464
// 删除 input.txt 文件
//输出
程序执行完毕
Error: ENOENT, open 'input.txt'

EventEmitter

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列

EventEmitter 类

events 模块只提供了一个对象: events.EventEmitter

EventEmitter 的核心就是事件触发与事件监听器功能的封装

1
2
3
4
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

EventEmitter 对象如果在实例化时发生错误,会触发 error 事件

当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 运行这段代码,1 秒后控制台输出了 'some_event 事件触发'
// 其原理是 event 对象注册了事件 some_event 的一个监听器
// 通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件 some_event,此时会调用some_event 的监听器
//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);
// 输出
// node event.js
some_event 事件触发

EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义

对于每个事件,EventEmitter 支持 若干个事件监听器

当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件
// 两个事件监听器回调函数被先后调用
//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 参数

EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件

方法

  • addListener(event, listener):为指定事件添加一个监听器到监听器数组的尾部

  • on(event, listener):为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数

1
2
3
server.on('connection', function (stream) {
console.log('someone connected!');
});
  • once(event, listener):为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器
1
2
3
server.once('connection', function (stream) {
console.log('Ah, we have our first user!');
});
  • removeListener(event, listener):移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器
1
2
3
4
5
6
var callback = function(stream) {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
  • removeAllListeners([event]):移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器

  • setMaxListeners(n):setMaxListeners 函数用于提高监听器的默认限制的数量

  • listeners(event):返回指定事件的监听器数组

  • emit(event, [arg1], [arg2], […]):按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false

类方法

  • listenerCount(emitter, event):返回指定事件的监听器数量

事件

  • newListener(emitter, event):该事件在添加新监听器时被触发

  • removeListener(emitter, event):从指定监听器数组中删除一个监听器

实例

通过 connection(连接)事件演示了 EventEmitter 类的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 创建 main.js 文件
var events = require('events');
var eventEmitter = new events.EventEmitter();
// 监听器 #1
var listener1 = function listener1() {
console.log('监听器 listener1 执行。');
}
// 监听器 #2
var listener2 = function listener2() {
console.log('监听器 listener2 执行。');
}
// 绑定 connection 事件,处理函数为 listener1
eventEmitter.addListener('connection', listener1);
// 绑定 connection 事件,处理函数为 listener2
eventEmitter.on('connection', listener2);
var eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 个监听器监听连接事件。");
// 处理 connection 事件
eventEmitter.emit('connection');
// 移除监绑定的 listener1 函数
eventEmitter.removeListener('connection', listener1);
console.log("listener1 不再受监听。");
// 触发连接事件
eventEmitter.emit('connection');
eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 个监听器监听连接事件。");
console.log("程序执行完毕。");
// 输出
// node main.js
2 个监听器监听连接事件。
监听器 listener1 执行。
监听器 listener2 执行。
listener1 不再受监听。
监听器 listener2 执行。
1 个监听器监听连接事件。
程序执行完毕。

error 事件

EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件

一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 的子类

Buffer(缓冲区)

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型

处理像TCP流或文件流时,必须使用到二进制数据

Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区

Buffer 与字符编码

Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据

1
2
3
4
5
6
7
const buf = Buffer.from('runoob', 'ascii');
// 输出 72756e6f6f62
console.log(buf.toString('hex'));
// 输出 cnVub29i
console.log(buf.toString('base64'));

Node.js 目前支持的字符编码包括

  • ascii - 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的
  • utf8 - 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8
  • utf16le - 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)
  • ucs2 - utf16le 的别名
  • base64 - Base64 编码
  • latin1 - 一种把 Buffer 编码成一字节编码的字符串的方式
  • binary - latin1 的别名
  • hex - 将每个字节编码为两个十六进制字

创建 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 实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建一个长度为 10、且用 0 填充的 Buffer。
const buf1 = Buffer.alloc(10);
// 创建一个长度为 10、且用 0x1 填充的 Buffer。
const buf2 = Buffer.alloc(10, 1);
// 创建一个长度为 10、且未初始化的 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');

写入缓冲区

buf.write(string[, offset[, length]][, encoding]),返回值:实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串

  • canshu - 写入缓冲区的字符串
  • offset - 缓冲区开始写入的索引值,默认为 0
  • length - 写入的字节数,默认为 buffer.length
  • encoding - 使用的编码。默认为 ‘utf8’
1
2
3
4
5
6
7
8
buf = Buffer.alloc(256);
len = buf.write("www.runoob.com");
console.log("写入字节数 : "+ len);
// 输出
// node main.js
写入字节数 : 14

从缓冲区读取数据

buf.toString([encoding[, start[, end]]]),返回值:解码缓冲区数据并使用指定的编码返回字符串

  • encoding - 使用的编码。默认为 ‘utf8’
  • start - 指定开始读取的索引位置,默认为 0
  • end - 结束位置,默认为缓冲区的末尾
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 对象

buf.toJSON(),返回值:返回 JSON 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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>

缓冲区合并

Buffer.concat(list[, totalLength]),返回一个多个成员合并的新 Buffer 对象

  • list - 用于合并的 Buffer 对象数组列表
  • totalLength - 指定合并后Buffer对象的总长度
1
2
3
4
5
6
7
var buffer1 = Buffer.from(('张扬教程'));
var buffer2 = Buffer.from(('www.zhangyang.com'));
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log("buffer3 内容: " + buffer3.toString());
// 输出
buffer3 内容: 张扬教程 www.zhangyang.com

缓冲区比较

buf.compare(otherBuffer),返回值:返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同

  • 与 buf 对象比较的另外一个 Buffer 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var buffer1 = Buffer.from('ABC');
var buffer2 = Buffer.from('ABCD');
var result = buffer1.compare(buffer2);
if(result < 0) {
console.log(buffer1 + " 在 " + buffer2 + "之前");
}else if(result == 0){
console.log(buffer1 + " 与 " + buffer2 + "相同");
}else {
console.log(buffer1 + " 在 " + buffer2 + "之后");
}
// 输出
ABC再ABCD之前

拷贝缓冲区

buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]),没有返回值

  • targetBuffer - 要拷贝的 Buffer 对象
  • targetStart - 数字, 可选, 默认: 0
  • sourceStart - 数字, 可选, 默认: 0
  • sourceEnd - 数字, 可选, 默认: buffer.length
1
2
3
4
5
6
7
8
9
10
var buf1 = Buffer.from('abcdefghijkl');
var buf2 = Buffer.from('RUNOOB');
//将 buf2 插入到 buf1 指定位置上
buf2.copy(buf1, 2);
console.log(buf1.toString());
// 输出
abRUNOOBijkl

缓冲区裁剪

buf.slice([start[, end]]),返回值:返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 start 到 end 的位置剪切

  • start - 数字, 可选, 默认: 0
  • end - 数字, 可选, 默认: buffer.length
1
2
3
4
5
6
7
var buffer1 = Buffer.from('runoob');
// 剪切缓冲区
var buffer2 = buffer1.slice(0,2);
console.log("buffer2 content: " + buffer2.toString());
// 输出
buffer2 content: ru

缓冲区长度

buf.length,返回值:返回 Buffer 对象所占据的内存长度

1
2
3
4
5
6
var buffer = Buffer.from('www.runoob.com');
// 缓冲区长度
console.log("buffer length: " + buffer.length);
// 输出
buffer length: 14

Stream(流)

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口

Node.js,Stream 有四种流类型

  • Readable - 可读操作
  • Writable - 可写操作
  • Duplex - 可读可写操作
  • Transform - 操作被写入数据,然后读出结果

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有

  • data - 当有数据可读时触发
  • end - 没有更多的数据可读时触发
  • error - 在接收和写入过程中发生错误时触发
  • finish - 所有数据已被写入到底层系统时触发

从流中读取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 创建 input.txt 文件
菜鸟教程官网地址:www.runoob.com
// 创建 main.js 文件
var fs = require("fs");
var data = '';
// 创建可读流
var readerStream = fs.createReadStream('input.txt');
// 设置编码为 utf8。
readerStream.setEncoding('UTF8');
// 处理流事件 --> data, end, and error
readerStream.on('data', function(chunk) {
data += chunk;
});
readerStream.on('end',function(){
console.log(data);
});
readerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
// 运行 node main.js
程序执行完毕
菜鸟教程官网地址:www.runoob.com

写入流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 创建 main.js 文件
var fs = require("fs");
var data = '菜鸟教程官网地址:www.runoob.com';
// 创建一个可以写入的流,写入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');
// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');
// 标记文件末尾
writerStream.end();
// 处理流事件 --> data, end, and error
writerStream.on('finish', function() {
console.log("写入完成。");
});
writerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
// 将 data 变量的数据写入到 output.txt 文件中
// 运行 node main.js
程序执行完毕
写入完成。
// 查看output.txt文件的内容
cat output.txt
菜鸟教程官网地址:www.runoob.com

管道流

管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 设置 input.txt 文件内容
菜鸟教程官网地址:www.runoob.com
管道流操作实例
// 创建 main.js 文件
var fs = require("fs");
// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');
// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');
// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);
console.log("程序执行完毕");
// node main.js
程序执行完毕
// 查看 output.txt 文件的内容
cat output.txt
菜鸟教程官网地址:www.runoob.com
管道流操作实例

链式流

链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作

用管道和链式来压缩和解压文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 当前目录下生成了 input.txt 的压缩文件 input.txt.gz
// 创建 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
文件压缩完成。

解压该文件,创建 decompress.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
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 提供了 exports 和 require 两个对象

  • exports 是模块公开的接口
  • require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象

创建模块

hello.js 通过 exports 对象把 world 作为模块的访问接口

main.js 中通过 require(‘./hello’) 加载这个模块

直接访 问 hello.js 中 exports 对象的成员函数

创建一个 main.js 文件

1
2
3
// require('./hello') 引入了当前目录下的 hello.js 文件
var hello = require('./hello');
hello.world();

创建 hello.js 文件

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

把一个对象封装到模块中

1
2
3
4
5
6
7
8
9
10
11
//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;

获得这个对象

1
2
3
4
5
//main.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();

服务端的模块加载

Node.js 的 require 方法中的文件查找策略:4 类模块(原生模块和3种文件模块)

从文件模块缓存中加载:尽管原生模块与文件模块的优先级不同,但是都会优先从文件模块的缓存中加载已经存在的模块

从原生模块加载:原生模块的优先级仅次于文件模块缓存的优先级。require 方法在解析文件名之后,优先检查模块是否在原生模块列表中

从文件加载:当文件模块缓存中不存在,而且不是原生模块的时候,Node.js会解析require方法传入的参数,并从文件系统中加载实际的文件

require方法接受以下几种参数的传递

  • http、fs、path等,原生模块
  • ./mod或../mod,相对路径的文件模块
  • /pathtomodule/mod,绝对路径的文件模块
  • mod,非原生模块的文件模块

函数

Node.js中函数的使用与Javascript类似

1
2
3
4
5
6
7
8
9
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
}
execute(say, "Hello");

匿名函数

没有名字的函数叫做匿名函数

1
2
3
4
5
function execute(someFunction, value) {
someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");

HTTP服务器工作机制

我们向 createServer 函数传递了一个匿名函数

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

用这样的代码也可以达到同样的目的

1
2
3
4
5
6
7
8
9
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);

路由

为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码

所有数据都会包含在 request 对象中,该对象作为 onRequest() 回调函数的第一个参数传递

解析这些数据,需要Node.JS 模块,它们分别是 url 和 querystring 模块

1
2
3
4
5
6
如:http://localhost:8888/start?foo=bar&hello=world
url.parse(string).query // foo=bar&hello=world
url.parse(string).pathname // start
querystring.parse(queryString)["foo"] // bar
querystring.parse(queryString)["hello"] // world

onRequest() 函数加上一些逻辑,用来找出浏览器请求的 URL 路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// server.js
var http = require("http");
var url = require("url");
function start() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;

编写路由,新建router.js

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

路由和服务器整合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 扩展一下服务器的 start() 函数,以便将路由函数作为参数传递过去
// server.js 文件
var http = require("http");
var url = require("url");
function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(pathname);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
// 相应扩展 index.js,使得路由函数可以被注入到服务器中
// index.js
var server = require("./server");
var router = require("./router");
server.start(router.route);
// 运行命令
node index.js
Server has started.

全局对象

Node.js 中的全局对象是 global,所有全局变量都是 global 对象的属性

__filename

当前正在执行的脚本的文件名,输出文件所在位置的绝对路径

1
2
3
4
5
6
7
// main.js
// 输出全局变量 __filename 的值
console.log( __filename );
// 运行命令行
node main.js
/web/com/runoob/nodejs/main.js

__dirname

当前执行脚本所在的目录

1
2
3
4
5
6
7
// main.js
// 输出全局变量 __dirname 的值
console.log( __dirname );
// 运行命令行
node main.js
/web/com/runoob/nodejs

process

当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口

常用的方法

  • exit: 当进程准备退出时触发
  • beforeExit: 当 node 清空事件循环,并且没有其他安排时触发这个事件
  • uncaughtException: 当一个异常冒泡回到事件循环,触发这个事件
  • Signal事件: 当进程接收到信号时就触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// main.js
process.on('exit', function(code) {
// 以下代码永远不会执行
setTimeout(function() {
console.log("该代码不会执行");
}, 0);
console.log('退出码为:', code);
});
console.log("程序执行结束");
运行命令行 node main.js
程序执行结束
退出码为: 0

退出状态码

  • 1-Uncaught Fatal Exception:有未捕获异常,并且没有被域或 uncaughtException 处理函数处理
  • 2-Unused:保留
  • 3-Internal JavaScript Parse Error:JavaScript的源码启动 Node 进程时引起解析错误
  • 4-Internal JavaScript Evaluation Failure:JavaScript 的源码启动 Node 进程,评估时返回函数失败
  • 5-Fatal Error:V8 里致命的不可恢复的错误。通常会打印到 stderr ,内容为: FATAL ERROR
  • 6-Non-function Internal Exception Handler:未捕获异常,内部异常处理函数不知为何设置为on-function,并且不能被调用
  • 7-Internal Exception Handler Run-Time Failure:未捕获的异常, 并且异常处理函数处理时自己抛出了异常
  • 8-Unused:保留
  • 9-Invalid Argument:可能是给了未知的参数,或者给的参数没有值
  • 10-Internal JavaScript Run-Time Failure:JavaScript的源码启动 Node 进程时抛出错误
  • 12-Invalid Debug Argument:设置了参数–debug 和/或 –debug-brk,但是选择了错误端口
  • 128-Signal Exits:如果 Node 接收到致命信号,比如SIGKILL 或 SIGHUP,那么退出代码就是128 加信号代码

Process 属性

Process 提供了很多有用的属性,便于我们更好的控制系统的交互(查文档)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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);
// 运行命令行
node main.js
Hello World!
0: node
1: /web/www/node/main.js
/usr/local/node/0.10.36/bin/node
darwin

常用工具

util 是一个Node.js 核心模块,提供常用函数的集合

util.inherits

util.inherits(constructor, superConstructor)是一个实现对象间原型继承 的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定义了一个基础对象Base 和一个继承自Base 的Sub,Base 有三个在构造函数 内定义的属性和一个原型中定义的函数,通过util.inherits 实现继承
var util = require('util');
function Base() {
this.name = 'base';
this.base = 1991;
this.sayHello = function() {
console.log('Hello ' + this.name);
};
}
Base.prototype.showName = function() {
console.log(this.name);
};
function Sub() {
this.name = 'sub';
}
util.inherits(Sub, Base);
var objBase = new Base();
objBase.showName();
objBase.sayHello();
console.log(objBase);
var objSub = new Sub();
objSub.showName();
//objSub.sayHello();
console.log(objSub);
// 运行命令行
base
Hello base
{ name: 'base', base: 1991, sayHello: [Function] }
sub
{ name: 'sub' }

util.inspect

util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出

至少接受一个参数 object,即要转换的对象

showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息

depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多少,默认2层

color 值为 true,输出格式将会以ANSI 颜色编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var util = require('util');
function Person() {
this.name = 'byvoid';
this.toString = function() {
return this.name;
};
}
var obj = new Person();
console.log(util.inspect(obj));
console.log(util.inspect(obj, true));
//运行命令行
Person { name: 'byvoid', toString: [Function] }
Person {
name: 'byvoid',
toString:
{ [Function]
[length]: 0,
[name]: '',
[arguments]: null,
[caller]: null,
[prototype]: { [constructor]: [Circular] } } }

util.isArray(object)

如果给定的参数 “object” 是一个数组返回true,否则返回false

1
2
3
4
5
6
7
8
var util = require('util');
util.isArray([])
// true
util.isArray(new Array)
// true
util.isArray({})
// false

util.isRegExp(object)

如果给定的参数 “object” 是一个正则表达式返回true,否则返回false

1
2
3
4
5
6
7
8
var util = require('util');
util.isRegExp(/some regexp/)
// true
util.isRegExp(new RegExp('another regexp'))
// true
util.isRegExp({})
// false

util.isDate(object)

如果给定的参数 “object” 是一个日期返回true,否则返回false

1
2
3
4
5
6
7
8
var util = require('util');
util.isDate(new Date())
// true
util.isDate(Date())
// false (without 'new' returns a String)
util.isDate({})
// false

util.isError(object)

如果给定的参数 “object” 是一个错误对象返回true,否则返回false

1
2
3
4
5
6
7
8
var util = require('util');
util.isError(new Error())
// true
util.isError(new TypeError())
// true
util.isError({ name: 'Error', message: 'an error occurred' })
// false

文件系统

Node.js 提供一组类似 UNIX(POSIX)标准的文件操作API

Node 导入文件系统模块(fs)

1
var fs = require("fs")

异步和同步

Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本

读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 创建 input.txt 文件
菜鸟教程官网地址:www.runoob.com
文件读取实例
// 创建 file.js 文件
var fs = require("fs");
// 异步读取
fs.readFile('input.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("异步读取: " + data.toString());
});
// 同步读取
var data = fs.readFileSync('input.txt');
console.log("同步读取: " + data.toString());
console.log("程序执行完毕。");
// 运行命令行
node file.js
同步读取: 菜鸟教程官网地址:www.runoob.com
文件读取实例
程序执行完毕。
异步读取: 菜鸟教程官网地址:www.runoob.com
文件读取实例

打开文件

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

  • path - 文件的路径
  • flags - 文件打开的行为。具体值详见下文
  • mode - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)
  • callback - 回调函数,带有两个参数如:callback(err, fd)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 创建 file.js 文件,并打开 input.txt 文件进行读写
var fs = require("fs");
// 异步打开文件
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打开成功!");
});
// 运行命令行
node file.js
准备打开文件!
文件打开成功!

获取文件信息

fs.stat(path, callback)

  • path - 文件路径
  • callback - 回调函数,带有两个参数如:(err, stats), stats 是 fs.Stats 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 创建 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)

  • file - 文件名或文件描述符
  • data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(流) 对象
  • options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8,模式为 0666 , flag 为 ‘w’
  • callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 创建 file.js
var fs = require("fs");
console.log("准备写入文件");
fs.writeFile('input.txt', '我是通 过fs.writeFile 写入文件的内容', function(err) {
if (err) {
return console.error(err);
}
console.log("数据写入成功!");
console.log("--------我是分割线-------------")
console.log("读取写入的数据!");
fs.readFile('input.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("异步读取文件数据: " + data.toString());
});
});
// 运行命令行
node file.js
准备写入文件
数据写入成功!
--------我是分割线-------------
读取写入的数据!
异步读取文件数据: 我是通 过fs.writeFile 写入文件的内容

读取文件

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

  • fd - 通过 fs.open() 方法返回的文件描述符
  • buffer - 数据写入的缓冲区
  • offset - 缓冲区写入的写入偏移量
  • length - 要从文件中读取的字节数
  • position - 文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取
  • callback - 回调函数,有三个参数err, bytesRead, buffer,err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// input.txt 文件内容
菜鸟教程官网地址:www.runoob.com
// 创建 file.js 文件
var fs = require("fs");
var buf = new Buffer.alloc(1024);
console.log("准备打开已存在的文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打开成功!");
console.log("准备读取文件:");
fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
if (err){
console.log(err);
}
console.log(bytes + " 字节被读取");
// 仅输出读取的字节
if(bytes > 0){
console.log(buf.slice(0, bytes).toString());
}
});
});
// 运行命令行
node file.js
准备打开已存在的文件!
文件打开成功!
准备读取文件:
42 字节被读取
菜鸟教程官网地址:www.runoob.com

关闭文件

fs.close(fd, callback)

  • fd - 通过 fs.open() 方法返回的文件描述符
  • callback - 回调函数,没有参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// input.txt
菜鸟教程官网地址:www.runoob.com
// 创建 file.js 文件
var fs = require("fs");
var buf = new Buffer.alloc(1024);
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打开成功!");
console.log("准备读取文件!");
fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
if (err){
console.log(err);
}
// 仅输出读取的字节
if(bytes > 0){
console.log(buf.slice(0, bytes).toString());
}
// 关闭文件
fs.close(fd, function(err){
if (err){
console.log(err);
}
console.log("文件关闭成功");
});
});
});
// 运行命令行
node file.js
准备打开文件!
文件打开成功!
准备读取文件!
菜鸟教程官网地址:www.runoob.com
文件关闭成功

截取文件

fs.ftruncate(fd, len, callback)

  • fd - 通过 fs.open() 方法返回的文件描述符
  • len - 文件内容截取的长度
  • callback - 回调函数,没有参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// input.txt
site:www.runoob.com
// file.js
var fs = require("fs");
var buf = new Buffer.alloc(1024);
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打开成功!");
console.log("截取了10字节后的文件内容。");
// 截取文件
fs.ftruncate(fd, 10, function(err){
if (err){
console.log(err);
}
console.log("文件截取成功。");
console.log("读取相同的文件");
fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
if (err){
console.log(err);
}
// 仅输出读取的字节
if(bytes > 0){
console.log(buf.slice(0, bytes).toString());
}
// 关闭文件
fs.close(fd, function(err){
if (err){
console.log(err);
}
console.log("文件关闭成功!");
});
});
});
});
// 运行命令行
node file.js
准备打开文件!
文件打开成功!
截取了10字节后的文件内容。
文件截取成功。
读取相同的文件
site:www.r
文件关闭成功

删除文件

fs.unlink(path, callback)

  • path - 文件路径
  • callback - 回调函数,没有参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// input.txt
site:www.runoob.com
// 创建 file.js
var fs = require("fs");
console.log("准备删除文件!");
fs.unlink('input.txt', function(err) {
if (err) {
return console.error(err);
}
console.log("文件删除成功!");
});
// 运行命令行
node file.js
准备删除文件!
文件删除成功!

创建目录

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

  • path - 文件路径
  • mode - 设置目录权限,默认为 0777
  • callback - 回调函数,没有参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fs = require("fs");
console.log("创建目录 /tmp/test/");
fs.mkdir("/tmp/test/",function(err){
if (err) {
return console.error(err);
}
console.log("目录创建成功。");
});
// 运行命令行
node file.js
创建目录 /tmp/test/
目录创建成功。

读取目录

fs.readdir(path, callback)

  • path - 文件路径
  • callback - 回调函数,回调函数带有两个参数err, files,err 为错误信息,files 为 目录下的文件数组列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建file.js
var fs = require("fs");
console.log("查看 /tmp 目录");
fs.readdir("/tmp/",function(err, files){
if (err) {
return console.error(err);
}
files.forEach( function (file){
console.log( file );
});
});
// 运行命令行
node file.js
查看 /tmp 目录
input.out
output.out
test
test.txt

删除目录

fs.rmdir(path, callback)

  • path - 文件路径
  • callback - 回调函数,没有参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建file.js
var fs = require("fs");
// 执行前创建一个空的 /tmp/test 目录
console.log("准备删除目录 /tmp/test");
fs.rmdir("/tmp/test",function(err){
if (err) {
return console.error(err);
}
console.log("读取 /tmp 目录");
fs.readdir("/tmp/",function(err, files){
if (err) {
return console.error(err);
}
files.forEach( function (file){
console.log( file );
});
});
});
// 运行命令行
node file.js
准备删除目录 /tmp/test
读取 /tmp 目录
……

GET/POST请求

表单提交到服务器一般都使用 GET/POST 请求

获取GET请求内容

node.js 中 url 模块中的 parse 函数提供了这个功能

1
2
3
4
5
6
7
8
9
// 在浏览器中访问 http://localhost:3000/user?name=菜鸟教程&url=www.runoob.com 然后查看返回结果
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);

使用 url.parse 方法来解析 URL 中的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 在浏览器中访问 http://localhost:3000/user?name=菜鸟教程&url=www.runoob.com 然后查看返回结果
var http = require('http');
var url = require('url');
var util = require('util');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
// 解析 url 参数
var params = url.parse(req.url, true).query;
res.write("网站名:" + params.name);
res.write("\n");
res.write("网站 URL:" + params.url);
res.end();
}).listen(3000);

获取 POST 请求内容

POST 请求的内容全部的都在请求体中,http.ServerRequest 并没有一个属性内容为请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// node.js 默认是不会解析请求体的,当你需要的时候,需要手动来做
var http = require('http');
var querystring = require('querystring');
http.createServer(function(req, res){
// 定义了一个post变量,用于暂存请求体的信息
var post = '';
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
req.on('data', function(chunk){
post += chunk;
});
// 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
req.on('end', function(){
post = querystring.parse(post);
res.end(util.inspect(post));
});
}).listen(3000);

实例表单通过 POST 提交并输出数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);

工具模块

OS模块

提供了一些基本的系统操作函数

var os = require(“os”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// main.js文件
var os = require("os");
// CPU 的字节序
console.log('endianness : ' + os.endianness());
// 操作系统名
console.log('type : ' + os.type());
// 操作系统名
console.log('platform : ' + os.platform());
// 系统内存总量
console.log('total memory : ' + os.totalmem() + " bytes.");
// 操作系统空闲内存量
console.log('free memory : ' + os.freemem() + " bytes.");
// 运行命令行
node main.js
endianness : LE
type : Linux
platform : linux
total memory : 25103400960 bytes.
free memory : 20676710400 bytes.

Path 模块

提供了一些用于处理文件路径的小工具

var path = require(“path”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 创建main.js
var path = require("path");
// 格式化路径
console.log('normalization : ' + path.normalize('/test/test1//2slashes/1slash/tab/..'));
// 连接路径
console.log('joint path : ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));
// 转换为绝对路径
console.log('resolve : ' + path.resolve('main.js'));
// 路径中文件的后缀名
console.log('ext name : ' + path.extname('main.js'));
// 运行命令行
node main.js
normalization : /test/test1/2slashes/1slash
joint path : /test/test1/2slashes/1slash
resolve : /web/com/1427176256_27423/main.js
ext name : .js

Net 模块

提供了一些用于底层的网络通信的小工具,包含了创建服务器/客户端的方法

var net = require(“net”)

net.Server通常用于创建一个 TCP 或本地服务器

net.Socket 对象是 TCP 或 UNIX Socket 的抽象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// server.js
var net = require('net');
var server = net.createServer(function(connection) {
console.log('client connected');
connection.on('end', function() {
console.log('客户端关闭连接');
});
connection.write('Hello World!\r\n');
connection.pipe(connection);
});
server.listen(8080, function() {
console.log('server is listening');
});
// 运行命令行
node server.js
server is listening # 服务已创建并监听 8080 端口

新开一个窗口,创建 client.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// client.js
var net = require('net');
var client = net.connect({port: 8080}, function() {
console.log('连接到服务器!');
});
client.on('data', function(data) {
console.log(data.toString());
client.end();
});
client.on('end', function() {
console.log('断开与服务器的连接');
});
// 运行命令行
连接到服务器!
Hello World!
断开与服务器的连接

DNS 模块

DNS 模块用于解析域名

var dns = require(“dns”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// main.js
var dns = require('dns');
dns.lookup('www.github.com', function onLookup(err, address, family) {
console.log('ip 地址:', address);
dns.reverse(address, function (err, hostnames) {
if (err) {
console.log(err.stack);
}
console.log('反向解析 ' + address + ': ' + JSON.stringify(hostnames));
});
});
// 运行命令行
address: 192.30.252.130
reverse for 192.30.252.130: ["github.com"]

Domain 模块

Domain(域) 简化异步代码的异常处理,可以捕捉处理try catch无法捕捉的异常

var domain = require(“domain”)

Domain 模块可分为隐式绑定和显式绑定

  • 隐式绑定: 把在domain上下文中定义的变量,自动绑定到domain对象
  • 显式绑定: 把不是在domain上下文中定义的变量,以代码的方式绑定到domain对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 创建 main.js
var EventEmitter = require("events").EventEmitter;
var domain = require("domain");
var emitter1 = new EventEmitter();
// 创建域
var domain1 = domain.create();
domain1.on('error', function(err){
console.log("domain1 处理这个错误 ("+err.message+")");
});
// 显式绑定
domain1.add(emitter1);
emitter1.on('error',function(err){
console.log("监听器处理此错误 ("+err.message+")");
});
emitter1.emit('error',new Error('通过监听器来处理'));
emitter1.removeAllListeners('error');
emitter1.emit('error',new Error('通过 domain1 处理'));
var domain2 = domain.create();
domain2.on('error', function(err){
console.log("domain2 处理这个错误 ("+err.message+")");
});
// 隐式绑定
domain2.run(function(){
var emitter2 = new EventEmitter();
emitter2.emit('error',new Error('通过 domain2 处理'));
});
domain1.remove(emitter1);
emitter1.emit('error', new Error('转换为异常,系统将崩溃!'));
// 运行命令行
监听器处理此错误 (通过监听器来处理)
domain1 处理这个错误 (通过 domain1 处理)
domain2 处理这个错误 (通过 domain2 处理)
events.js:72
throw er; // Unhandled 'error' event
^
Error: 转换为异常,系统将崩溃!
at Object.<anonymous> (/www/node/main.js:40:24)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:929:3

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 最基本的 HTTP 服务器架构(使用 8080 端口)
// 创建 server.js
var http = require('http');
var fs = require('fs');
var url = require('url');
// 创建服务器
http.createServer( function (request, response) {
// 解析请求,包括文件名
var pathname = url.parse(request.url).pathname;
// 输出请求的文件名
console.log("Request for " + pathname + " received.");
// 从文件系统中读取请求的文件内容
fs.readFile(pathname.substr(1), function (err, data) {
if (err) {
console.log(err);
// HTTP 状态码: 404 : NOT FOUND
// Content Type: text/plain
response.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 状态码: 200 : OK
// Content Type: text/plain
response.writeHead(200, {'Content-Type': 'text/html'});
// 响应文件内容
response.write(data.toString());
}
// 发送响应数据
response.end();
});
}).listen(8080);
// 控制台会输出以下信息
console.log('Server running at http://127.0.0.1:8080/');
// 创建一个 index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<h1>我的第一个标题</h1>
<p>我的第一个段落。</p>
</body>
</html>
// 运行命令行
node server.js
Server running at http://127.0.0.1:8080/

打开地址:http://127.0.0.1:8080/index.html

1
2
3
// 执行 server.js 的控制台输出信息
Server running at http://127.0.0.1:8080/
Request for /index.html received. # 客户端请求信息

Node 创建 Web 客户端

Node 创建 Web 客户端需要引入 http 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 创建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();
// 运行命令行
node client.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<h1>我的第一个标题</h1>
<p>我的第一个段落。</p>
</body>
</html>
// 执行 server.js 的控制台输出信息
Server running at http://127.0.0.1:8080/
Request for /index.html received. # 客户端请求信息

RESTful API

REST即表述性状态传递

表述性状态转移是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful

REST 通常使用 JSON 数据格式

REST 基本架构的四个方法

  • GET - 用于获取数据
  • PUT - 用于更新或添加数据
  • DELETE - 用于删除数据
  • POST - 用于添加数据

RESTful Web Services

基于 REST 架构的 Web Services 即是 RESTful

由于轻量级以及通过 HTTP 直接传输数据的特性,Web 服务的 RESTful 方法已经成为最常见的替代方法

可以使用各种语言(比如 Java 程序、Perl、Ruby、Python、PHP 和 Javascript[包括 Ajax])实现客户端

创建 RESTful

创建一个 json 数据资源文件 users.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"user1" : {
"name" : "mahesh",
"password" : "password1",
"profession" : "teacher",
"id": 1
},
"user2" : {
"name" : "suresh",
"password" : "password2",
"profession" : "librarian",
"id": 2
},
"user3" : {
"name" : "ramesh",
"password" : "password3",
"profession" : "clerk",
"id": 3
}
}

基于以上数据,我们创建以下 RESTful API

序号 URI HTTP方法 发送内容 结果
1 listUsers GET 显示所有用户列表
2 addUser POST JSON 字符串 添加新用户
3 deleteUser DELETE JSON 字符串 删除用户
4 :id GET 显示用户详细信息

获取用户列表

创建了 RESTful API listUsers,用于读取用户的信息列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// server.js
var express = require('express');
var app = express();
var fs = require("fs");
app.get('/listUsers', function (req, res) {
fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
console.log( data );
res.end( data );
});
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
})
// 运行命令行
node server.js
应用实例,访问地址为 http://0.0.0.0:8081
// 在浏览器中访问 http://127.0.0.1:8081/listUsers
{
"user1" : {
"name" : "mahesh",
"password" : "password1",
"profession" : "teacher",
"id": 1
},
"user2" : {
"name" : "suresh",
"password" : "password2",
"profession" : "librarian",
"id": 2
},
"user3" : {
"name" : "ramesh",
"password" : "password3",
"profession" : "clerk",
"id": 3
}
}

添加用户

创建了 RESTful API addUser, 用于添加新的用户数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// server.js
var express = require('express');
var app = express();
var fs = require("fs");
//添加的新用户数据
var user = {
"user4" : {
"name" : "mohit",
"password" : "password4",
"profession" : "teacher",
"id": 4
}
}
app.get('/addUser', function (req, res) {
// 读取已存在的数据
fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
data = JSON.parse( data );
data["user4"] = user["user4"];
console.log( data );
res.end( JSON.stringify(data));
});
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
})
// 运行命令行
node server.js
应用实例,访问地址为 http://0.0.0.0:8081
// 在浏览器中访问 http://127.0.0.1:8081/addUser
{ user1:
{ name: 'mahesh',
password: 'password1',
profession: 'teacher',
id: 1 },
user2:
{ name: 'suresh',
password: 'password2',
profession: 'librarian',
id: 2 },
user3:
{ name: 'ramesh',
password: 'password3',
profession: 'clerk',
id: 3 },
user4:
{ name: 'mohit',
password: 'password4',
profession: 'teacher',
id: 4 }
}

显示用户详情

创建了 RESTful API :id(用户id), 用于读取指定用户的详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// server.js
var express = require('express');
var app = express();
var fs = require("fs");
app.get('/:id', function (req, res) {
// 首先我们读取已存在的用户
fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
data = JSON.parse( data );
var user = data["user" + req.params.id]
console.log( user );
res.end( JSON.stringify(user));
});
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
})
// 运行命令行
node server.js
应用实例,访问地址为 http://0.0.0.0:8081
// 在浏览器中访问 http://127.0.0.1:8081/2
{
"name":"suresh",
"password":"password2",
"profession":"librarian",
"id":2
}

删除用户

创建了 RESTful API deleteUser, 用于删除指定用户的详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// server.js
var express = require('express');
var app = express();
var fs = require("fs");
var id = 2;
app.get('/deleteUser', function (req, res) {
// First read existing users.
fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
data = JSON.parse( data );
delete data["user" + 2];
console.log( data );
res.end( JSON.stringify(data));
});
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
})
// 运行命令行
node server.js
应用实例,访问地址为 http://0.0.0.0:8081
// 在浏览器中访问 http://127.0.0.1:8081/deleteUser
{ user1:
{ name: 'mahesh',
password: 'password1',
profession: 'teacher',
id: 1 },
user3:
{ name: 'ramesh',
password: 'password3',
profession: 'clerk',
id: 3 }
}

多进程

Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发

每个子进程总是带有三个流对象:child.stdin, child.stdout 和child.stderr

Node 提供了 child_process 模块来创建子进程,方法有

  • exec - child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回
  • spawn - child_process.spawn 使用指定的命令行参数创建新进程
  • fork - child_process.fork 是 spawn()的特殊形式,用于在子进程中运行的模块,如 fork(‘./son.js’) 相当于 spawn(‘node’, [‘./son.js’])

exec() 方法

child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回

exec() 方法返回最大的缓冲区,并等待进程结束,一次性返回缓冲区的内容

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

  • command: 字符串, 将要运行的命令,参数使用空格隔开
  • options :对象
  • callback :回调函数,包含三个参数error, stdout 和 stderr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 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 使用指定的命令行参数创建新进程

spawn() 方法返回流 (stdout & stderr),在进程返回大量数据时使用。进程一旦开始执行时 spawn() 就开始接收响应

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

  • command: 将要运行的命令
  • args: Array 字符串参数数组
  • options Object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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);
});
}
// 运行命令行
node master.js stdout: 进程 0 执行。
子进程已退出,退出码 0
stdout: 进程 1 执行。
子进程已退出,退出码 0
stdout: 进程 2 执行。
子进程已退出,退出码 0

fork 方法

child_process.fork 是 spawn() 方法的特殊形式,用于创建进程

返回的对象除了拥有ChildProcess实例的所有方法,还有一个内建的通信信道

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

  • modulePath: String,将要在子进程中运行的模块
  • args: Array 字符串参数数组
  • options:Object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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);
});
}
// 运行命令行
node master.js
进程 0 执行。
子进程已退出,退出码 0
进程 1 执行。
子进程已退出,退出码 0
进程 2 执行。
子进程已退出,退出码 0

JXcore打包

JXcore 是一个支持多线程的 Node.js 发行版本

JXcore安装

下载 JXcore 安装包,并解压,在解压的的目录下提供了 jx 二进制文件命令

包代码

Node.js 项目包含以下几个文件,其中 index.js 是主文件

1
2
3
4
5
6
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 项目的主文件

1
jx package index.js index

命令执行成功,会生成以下两个文件

  • index.jxp 这是一个中间件文件,包含了需要编译的完整项目信息
  • index.jx 这是一个完整包信息的二进制文件,可运行在客户端上

载入 JX 文件

Node.js 的项目运行

1
node index.js command_line_arguments

使用 JXcore 编译后,我们可以使用以下命令来执行生成的 jx 二进制文件

1
jx index.jx command_line_arguments

连接 MySQL

连接 MongoDB