nodejs笔记

nodejs简介

  • 运行在服务器端的JavaScript
  • 基于事件驱动
  • 基于Google的V8引擎,执行JavaScript的性能很好

安装

ubuntu

  • 使用编译好的安装包
    1. nodejs官网下载合适的版本并解压
    2. 将nodejs/bin/npm,nodejs/bin/node软连接到/usr/local/bin/下
  • 使用源码安装
    1. 在github下载源码
    2. 在configure下sudo make,sudo make install
  • apt命令安装
    • sudo apt-get install nodejs
    • sudo apt-get install npm

mac

  • 源码安装
  • brew安装
    • brew install node

Hello World

  • 脚本格式

    1
    2
    echo "conole.log('Hello World!');" > helloWorld.js
    node helloWorld.js
  • shell模式

    1
    2
    ~$ node
    > console.log("Hello World!");

第一个应用

nodejs自行创建web服务器,可以监听客户端HTTP请求
不像很多语言需要借助apache或者nginx的帮助

编写服务器代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 需要库文件
var http = require('http');

http.createServer(function (request, response) {
// 服务器功能即为对每个request,都返回一个头部为200,内容为Hello World的响应

// 发送 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/');

启动服务器

1
node server.js

使用

  1. 服务器访问 http://127.0.0.1:8888
  2. 看到响应 Hollo World

npm介绍

类似pip之于python,不过npm有全局安装和本地安装的区别

安装与更新

  • 搜索模块

    1
    npm search <Module Name>
  • 本地安装

    1
    npm install <Module Name>

    会在路径下生成一个node~modules文件夹~
    程序中即可使用require(‘<Module Name>’)

  • 全局安装

    1
    npm install <Module Name> -g

    可能放在/usr/local/node-xxx/lib/module/modules/下

  • 更新模块

    1
    npm update <Module Name>
  • 卸载模块

    1
    npm uninstall <Module Name>

包信息

  • 查看安装列表

    1
    npm list -g  #或者npm ls -g

    第一行输出的是模块安装的位置

  • 查看特定模块版本

    1
    npm list <Module Name>
  • 模块说明文档

    • /<Module Name>/package.json
    • 包含了包名,版本号,描述,官网,作者,贡献者,依赖列表,main方法,关键字等信息

创建模块

1
npm init

会生成package.json模板,包含必要的信息

  • 版本号
    • 定义为X.Y.Z
    • 修复bug更改Z
    • 新功能,向下兼容,更改Y
    • 大变动,向下不兼容,更改X

npm其他常用命令

  • npm help
  • npm help <command>
  • npm publish
  • npm unpublish <package>@<version>
  • npm cache clear

REPL

Read Eval Print Loop
交互式解释器
即shell模式
使用方法类似python
其他:

  • 使用 =_=代表ans
  • .help 列出使用命令
  • .break 退出多行表达式
  • .clear 退出多行表达式
  • .save <filename> 保存当前会话至文件
  • .load <filename> 从文件载入会话内容

回调函数

简介

nodejs所有的API都支持回调函数
从而减少了阻塞的使用
提高了性能,可以处理大量并发请求

使用

一般作为函数的最后一个参数出现

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

对比

假设一个文件内容为

菜鸟教程官网地址:www.runoob.com
  1. 阻塞写法

    注意这里的读取文件的函数会产生阻塞

    1
    2
    3
    4
    5
    6
    var fs = require("fs");

    var data = fs.readFileSync('input.txt');

    console.log(data.toString());
    console.log("程序执行结束!");

    读取文件结束后才继续执行

  2. 非阻塞写法

    1
    2
    3
    4
    5
    6
    7
    8
    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中的事件机制使用的是观察者模式

事件循环描述:

  • 单线程类进入到wihle(true) 循环
  • 在异步式处理下,每个请求有可能对应一个异步线程,即生成一个观察者,有事件发生就被通知,然后调用回调函数处理
  • 当没有观察者时循环结束

所以也称Node.js是事件驱动
注意:

  • nodejs可以对某一个函数定义回调函数,也可以对某一个事件定义回调函数
  • 某些函数可以产生几种内置的事件,需要多注意文档

events模块的使用

核心的是eventEmitter对象,具有

  • 绑定并监听事件
  • 触发事件
  • 事件列表管理

等功能

  1. 基础用法

    1. 例子一:完全使用自定义事件

      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
      / 引入 events 模块
      var events = require('events');
      // 创建 eventEmitter 对象
      var eventEmitter = new events.EventEmitter();

      // 在外部创建eventHandler
      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("程序执行完毕。");
    2. 例子二:带有内置事件

  2. 事件的触发

    注意

    • 同一个事件可以有多个Handler,触发时按照注册的顺序
    • 触发事件时可以带有参数,被回调函数所使用
      • 所以有些回调函数中出现的莫名的参数列表就是调用该回调函数的函数传出的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    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 参数');
  3. EventEmitter的方法和其他

    • 对象方法
    方法 描述
    addListener(event,listener) 添加监听器到某事件监听器队列的尾部
    on(event,listener) 也是添加下一个监听器,不同点不明
    once(event,listenter) 添加一个单次监听器,效果是最多触发一次
    removeListener(event,listener) 需要被移除的是非匿名的?
    removeAllListener([event]) 不指定事件则移除所有事件的监听器
    setMaxListeners(n) 为了性能,指定一个上限用于输出警告信息,默认为10
    listeners(event) 返回监听器数组
    listenerCount(event) 返回监听器数组中的数量
    emit(event,[arg1],[arg2],…) 若事件没有监听器则返回false
    • 类方法
      events.EventEmitter.listenerCount(emitter,eventName)
      由于是静态方法,涉及到类,已经废弃
    • 触发的事件
      • newListener
      • removeListener
    • 默认的事件
      • error 强制系统返回错误
  4. 为什么某些函数会产生默认的事件

    fs,net,http等继承了EventEmitter,不是通过类而是通过原型

Buffer

js中没有定义二进制数据相关的类型
nodejs中加入buffer类来实现扩展
在内存上也使用V8引擎堆外部的区域

创建方法

  • 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 实例

写入与读取

  • 写入
    buf.write(string[, offset[, length]][, encoding])
    参数:
    • length默认为string.lenght,也可以手动指定
  • 读取
    buf.toString([encoding[, start[, end]]])

转化等其他操作

  • 转换为JSON对象
    buffer.toJSON()
    可以转换为{“type”:“Buffer”,“data”:[xxx]}的JSON数据
  • 合并
    Buffer.concat([buf1,buf2,buf3…],[]totalLength)
  • 比较
    buffer.compare(otherBuffer)
    返回值
    负数表示buffer在字符的排序上在otherBuffer前,比如ABC在ABCD之前
    0表示相同
    整数表示buffer在otherBuffer之后
  • 拷贝
    buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
    • targetStart与sourceStart区别不明
  • 裁剪
    buf.slice([start[, end]])
  • 获取长度
    buffer.length

还有文字编码转换的用处

建立时指定字符编码,输出时选择不同的编码即可

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

Stream

一个抽象的接口,许多对象是其实现,request,response,stdout等
按照读写类型可以有不同实现

  • Readable
  • Writable
  • Duplex
  • Transform

由于Stream还继承了EventEmitter,因此可以触发几个事件

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

常用操作

  1. 读取数据

    这里看到,事件的触发隐藏在readerStream类的构造方法中
    而事件的Handler需要手动编写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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("程序执行完毕");
  2. 写入数据

    这里使用了finish与error两种事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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("程序执行完毕");
  3. 管道传递

    传递一次称为管道流

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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("程序执行完毕");

    多次传递,称为链式流?
    这里使用压缩举例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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("文件压缩完成。");

    还有解压的例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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("文件解压完成。");

模块系统

nodejs将文件视为模块,使文件可以使用require互相调用

使用举例

被引用的模块使用exports向外部暴露信息

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

引用的模块使用require调用模块
可以使用相对路径

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

也有直接对exports赋值的

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;

这时无需使用moduleName.functionName来调用函数
可以直接使用moduleName作为functionName

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

内置模块与加载顺序

http等都是内置的模块
加载时的顺序满足:

  • 先看缓冲再看文件(内置模块也有其文件)
  • 优先内置模块
  • 载入模块后会缓存以便于下次查找

函数

同js
函数可以作为变量使用
因此函数的参数中可以包含函数
也因此,最多的表现为使用匿名函数作为参数使用

路由

路由即为根据path(即网址)的不同,做出不同的响应
需要的功能为从http请求中path的部分的提取

url模块简介

path对象在url.parse(string).pathname中存储
其实参数在url.parse(string).query中存储,使用 .<paraName> 即可获取,不过该功能与路由暂时无关

使用举例

例子中使用依赖注入的方法,将router独立出来,便于修改
并用第三者文件调用router与server的功能

这里的router根据不同网址做出的响应仅仅是输出
没有加太多的判断

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

exports.route = route;

这里的server使用url包提取了request.url中的path信息,并传递给路由

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

第三方文件中使用require调用了两个文件

1
2
3
4
var server = require("./server");
var router = require("./router");

server.start(router.route);

全局对象

global对象

由于文件被视为模块,其中定义的变量很难位于最外层
于是规定一个全局对象global作为全局变量的宿主
定义在其下的变量可以全局访问
因此全局变量的概念也扩展到

  • 最外层定义的变量
  • 全局对象的属性
  • 隐式定义的变量(未定义直接赋值)(由于变量提升)

几个常量

  • _~filename~
    当前脚本的绝对路径与文件名
  • _~dirname~
    当前脚本的绝对路径

几个全局函数

或许叫内置函数更合适

  • setTimeout(function,ms~time~)
  • clearTimeout(counterName)
  • setInterval(function,ms~time~)

几个全局对象

  1. console

    操作与控制台相关的行为
    方法有:

    • console.log(data,…)
      console.log(‘%d’,1991)
    • console.info(data,…)与log差别不大,有时除了信息外显示一个蓝色的感叹号
    • console.error(data,…)也是信息,不过用红色感叹号表示错误
    • console.warn(data,…)黄色
    • console.dir(obj,[options]) 检查一个对象,使易于阅读和打印提示
    • console.time(label)计时开始
    • console.timeEnd(label)计时结束,并输出所用时间
    • console.trace(message,…)代码在堆中的调用路径,用于测试代码
    • console.assert(value,message,…)判断某个变量是否为真,若为false则输出message
  2. process

    用于描述当前进程
    也是EventEmitter的实现,可以触发一些事件

    • exit 进程退出时触发
    • beforeExit 事件循环为空时触发
    • uncaughtException默认的错误对应的事件,为此事件加了监视器则程序不会退出
    • Signal进程收到信号时触发

    举例:退出码的显示
    一部分代码
    process.on函数在出发exit事件时还带有了参数code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    process.on('exit', function(code) {

    // 以下代码永远不会执行
    setTimeout(function() {
    console.log("该代码不会执行");
    }, 0);

    console.log('退出码为:', code);
    });
    console.log("程序执行结束");

    退出码如下

    状态码 名称 描述
    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还有一些属性如下

    属性 描述
    stdout 标准输出流。
    stderr 标准错误流。
    stdin 标准输入流。
    argv argv 属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是node,第二个成员是脚本文件名,其余成员是脚本文件的参数。
    execPath 返回执行当前脚本的 Node 二进制文件的绝对路径。
    execArgv 返回一个数组,成员是命令行下执行脚本时,在Node可执行文件与脚本文件之间的命令行参数。
    env 返回一个对象,成员为当前 shell 的环境变量
    exitCode 进程退出时的代码,如果进程优通过 process.exit() 退出,不需要指定退出码。
    version Node 的版本,比如v0.10.18。
    versions 一个属性,包含了 node 的版本和依赖.
    config 一个包含用来编译当前 node 执行文件的 javascript 配置选项的对象。它与运行 ./configure 脚本生成的 “config.gypi” 文件相同。
    pid 当前进程的进程号。
    title 进程名,默认值为"node",可以自定义该值。
    arch 当前 CPU 的架构:‘arm’、‘ia32’ 或者 ‘x64’。
    platform 运行程序所在的平台系统 ‘darwin’, ‘freebsd’, ‘linux’, ‘sunos’ 或 ‘win32’
    mainModule require.main 的备选方法。不同点,如果主模块在运行时改变,require.main可能会继续返回老的模块。可以认为,这两者引用了同一个模块。

    使用process.execPath等形式即可代表具体的变量内容

    process还有很多方法

    方法 描述
    abort() 这将导致node触发abort事件会让node退出并生成一个核心文件
    chdir(directory) 改变当前工作进程的目录,如果操作失败抛出异常
    cwd() 返回当前进程的工作目录
    exit([code]) 使用指定的code结束进程如果忽略,将会使用code0
    getgid() 获取进程的群组标识(参见getgid(2))获取到得时群组的数字id,而不是名字 注意:这个函数仅在POSIX平台上可用(例如,非Windows和Android)
    setgid(id) 设置进程的群组标识(参见setgid(2))可以接收数字ID或者群组名如果指定了群组名,会阻塞等待解析为数字ID
    getuid() 获取进程的用户标识(参见getuid(2))这是数字的用户id,不是用户名
    setuid(id) 设置进程的用户标识(参见setuid(2))接收数字ID或字符串名字果指定了群组名,会阻塞等待解析为数字ID
    getgroups() 返回进程的群组iD数组POSIX系统没有保证一定有,但是node.js保证有
    setgroups(groups) 设置进程的群组ID这是授权操作,所以你需要有root权限,或者有CAP~SETGID能力~
    initgroups(user,extra~group~) 读取/etc/group,并初始化群组访问列表,使用成员所在的所有群组这是授权操作,所以你需要有root权限,或者有CAP~SETGID能力~
    kill(pid[,signal]) 发送信号给进程.pid是进程id,并且signal是发送的信号的字符串描述信号名是字符串,比如’SIGINT’或’SIGHUP’如果忽略,信号会是’SIGTERM’
    memoryUsage() 返回一个对象,描述了Node进程所用的内存状况,单位为字节
    nextTick(callback) 一旦当前事件循环结束,调用回调函数
    umask([mask]) 设置或读取进程文件的掩码子进程从父进程继承掩码如果mask参数有效,返回旧的掩码否则,返回当前掩码
    uptime() 返回Node已经运行的秒数
    hrtime() 返回当前进程的高分辨时间,形式为[seconds,nanoseconds]数组它是相对于过去的任意事件该值与日期无关,因此不受时钟漂移的影响主要用途是可以通过精确的时间间隔,来衡量程序的性能.你可以将之前的结果传递给当前的process.hrtime(),会返回两者间的时间差,用来基准和测量时间间隔

    其中id,uid,groups相关的都仅在POSIX平台上可用(例如,非Windows和Android)
    都是与系统交互的功能

常用工具

util是nodejs的核心模块,包含了许多常用的工具

原型继承

util.inherits()方法
例子中有一个"父类"Base,其有三个成员与一个sayHello方法
不过在其原型中又定义了一个showName方法
"子类"是sub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);

可以看出:

  • sub仅仅继承了原型中定义的showName()方法
  • showName()方法需要的this.name也必须是sub自己准备,sub不继承构造方法中的成员与方法
    • 使用sayHello()方法会报错

对象转字符串

  • 用法

    1
    util.inspect(object,[showHidden],[depth],[colors])
  • 参数

    • object 对象
    • showHidden 是否显示隐藏信息
    • depth 若对象复杂,限制最大的递归层数,默认为2,设为null表示无上限
    • color 使用ANSI颜色编码,更漂亮
  • 注意
    不是简单使用toString方法

  • 举例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Person { name: 'byvoid', toString: [Function] }
    Person {
    name: 'byvoid',
    toString:
    { [Function]
    [length]: 0,
    [name]: '',
    [arguments]: null,
    [caller]: null,
    [prototype]: { [constructor]: [Circular] } } }

常用判断功能

  • util.isArray()
  • util.isRegExp()
  • util.isDate()
  • util.isError()

文件操作

fs模块包括了文件和文件夹的一些操作
几乎所有操作都有同步和异步版本

读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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("程序执行完毕。");

打开文件

  1. 用法

    1
    fs.open(path, flags[, mode], callback)
  2. 参数说明

    flags: 权限相关的模式

    Flag 描述
    r 以读取模式打开文件。如果文件不存在抛出异常。
    r+ 以读写模式打开文件。如果文件不存在抛出异常。
    rs 以同步的方式读取文件。
    rs+ 以同步的方式读取和写入文件。
    w 以写入模式打开文件,如果文件不存在则创建。
    wx 类似 ‘w’,但是如果文件路径存在,则文件写入失败。
    w+ 以读写模式打开文件,如果文件不存在则创建。
    wx+ 类似 ‘w+’, 但是如果文件路径存在,则文件读写失败。
    a 以追加模式打开文件,如果文件不存在则创建。
    ax 类似 ‘a’, 但是如果文件路径存在,则文件追加失败。
    a+ 以读取追加模式打开文件,如果文件不存在则创建。
    ax+ 类似 ‘a+’, 但是如果文件路径存在,则文件读取追加失败。

获取文件信息

  1. 用法

    1
    fs.stat(path,callback)
  2. 举例

    1
    2
    3
    4
    5
    var fs = require('fs');

    fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) {
    console.log(stats.isFile()); //true
    })
  3. 返回值说明

    返回一个stat对象,该对象中含有文件的信息,但需要使用响应方法才可输出

    方法 描述
    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。

写入文件

  1. 用法

    1
    fs.writeFile(file, data[, options], callback)
  2. 参数说明

    options:
    包含了encoding,mode,flay.默然为utf8,0666,‘w’

读取文件

  1. 用法

    1
    fs.read(fd, buffer, offset, length, position, callback)
  2. 参数说明

    • fd 通过fs.open()返回的文件描述符
    • buffer 写入的缓冲区
    • offset 缓冲区写入时的偏移量
    • length 从文件中读取的字节数
    • position 文件去读的起始位置,若为null,则会从当前指针的位置读取

关闭文件

  1. 用法

    1
    fs.close(fd, callback)
  2. 参数

    fd为文件描述符

截取文件

1
fs.ftruncate(fd,len,callback)

删除文件

1
fs.unlink(path, callback)

创建目录

  1. 用法

    1
    fs.mkdir(path[, mode], callback)
  2. 参数说明

    mode默认0777

读取目录

1
fs.readdir(path, callback)

但不知道读取了有什么用

删除目录

1
fs.rmdir(path, callback)

还有其他许多用法

GET/POST参数提取

需要从http请求中读取参数
两个模块都可以完成这些功能

  • url
  • querystring

GET请求

1
2
params = url.parse(req.url, true).query;
console.log(params.key1)

POST请求

1
2
result = querystring.parse(body);
console.log(result.key1)

完整代码

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 系统操作函数

Path 文件路径处理

Net 网络通信

DNS 解析域名

Domain 简化异步代码的异常处理

http模块

  1. 作为服务器

    nodejs可以自己建立web服务器,无需借助Apache,Nginx,IIS等
    一个静态网页服务器的例子
    从url中提取需要的文件的位置
    然后读取文件并输出到响应中

    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
    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/');
  2. 作为客户端

    可以使用request方法发出请求

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

Express框架

一个基于nodejs的web应用框架,有许多便利的API以便于搭建应用

安装

1
2
3
4
npm install express --save  
npm install body-parser --save # 处理JSON等
npm install cookie-parser --save # 解析cookie成为对象
npm install multer --save # 处理表单

Hello World

以express原型为中心

  • get方法建立对GET请求的响应服务
    • 其中第一个参数设置路径
    • 自然有两个参数可以使用(req,res)
  • listen方法开始监听(指定了端口)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//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)

})

访问127.0.0.1:8081可得到Hello World

request对象的属性

  • 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类型

response对象的属性

  • 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类型

路由

get方法的参数中设置不同的路径,在回调函数中设置相应的动作

静态文件

静态文件的访问
在服务器上的文件,如何使用url中的path访问的问题
使用express.static(‘folderName’)即可

假设有如下文件结构

1
2
3
4
5
node_modules
server.js
public/
public/images
public/images/logo.png

而server.js如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var express = require('express');
var app = express();

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

app.get('/', function (req, res) {
res.send('Hello World');
})

var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

使用浏览器访问127.0.0.1:8081/images/logo.png时即可看到需要的文件

GET请求参数的快捷读取

这里使用了其他的中间件自动处理,可以直接使用
req.query.key1等格式直接使用请求中的参数

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
var express = require('express');
var app = express();

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

app.get('/index.htm', function (req, res) {
res.sendFile( __dirname + "/" + "index.htm" );
})

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

// 输出 JSON 格式
var response = {
"first_name":req.query.first_name,
"last_name":req.query.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})

var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

POST请求参数的快捷读取

其他基本同GET
需要使用body-paraser对放在body中的POST参数进行解码
然后也可以直接使用req.body.key1等格式

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
var express = require('express');
var app = express();
var bodyParser = require('body-parser');

// 创建 application/x-www-form-urlencoded 编码解析
var urlencodedParser = bodyParser.urlencoded({ extended: false })

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

app.get('/index.htm', function (req, res) {
res.sendFile( __dirname + "/" + "index.htm" );
})

app.post('/process_post', urlencodedParser, function (req, res) {

// 输出 JSON 格式
var response = {
"first_name":req.body.first_name,
"last_name":req.body.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})

var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

文件上传

核心为读取req中文件的数据
然后写入到同名的本地的文件中

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
var express = require('express');
var app = express();
var fs = require("fs");

var bodyParser = require('body-parser');
var multer = require('multer');

app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(multer({ dest: '/tmp/'}).array('image'));

app.get('/index.htm', function (req, res) {
res.sendFile( __dirname + "/" + "index.htm" );
})

app.post('/file_upload', function (req, res) {

console.log(req.files[0]); // 上传的文件信息

var des_file = __dirname + "/" + req.files[0].originalname;
fs.readFile( req.files[0].path, function (err, data) {
fs.writeFile(des_file, data, function (err) {
if( err ){
console.log( err );
}else{
response = {
message:'File uploaded successfully',
filename:req.files[0].originalname
};
}
console.log( response );
res.end( JSON.stringify( response ) );
});
});
})

var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

Cookies管理

读取cookies时从req中读取即可

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

RESTful API

REST介绍

Representational State Transfer
表述性状态传递
一种软件架构风格
一组约束条件和原则
满足的话称为RESTful
通常使用json保存数据

其中一个条件是需要有4个基本的接口

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

示例

  1. 数据

    假设有一组数据如下

    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
    }
    }
  2. 计划

    对该数据进行操作为例
    先定好需要做的4个功能,其中有PUT接口没有实现,原因不明

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

    过滤器指定listUser为URI,在访问该地址时,
    读取数据文件内容并直接显示

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

    })
  4. POST/PUT

    在匹配到addUser地址时
    使用JSON对象的API完成对文档的编辑

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

    })
  5. 常用的检索功能,应该也符合REST风格

    还是使用JSON对象直接读取文件中指定大纲的数据

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

    })

    访问127.0.0.1:8081/2时输出user2的数据

  6. DELETE

    还是依靠JSON的API,像数组一样操作文档

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

    })

多进程

nodejs是单线程但却可以是多进程
有部分手动创建进程的方法

  • exec
  • spawn
  • fork

nodejs单线程,可以是多进程
这里是手动创建多进程的方法
用到的包为child~process~

exec

  • 特点: 像是最基础的用法

  • 用法

    1
    child_process.exec(command[, options], callback)
    • command刚像是shell命令
    • options中可以设置环境变量键值对,encoding,shell,timeout,maxBuffer,uid,gid等
    • callback固定参数为error,stdout,stderr?
  • 举例
    被调用的文件

    1
    console.log("进程 " + process.argv[2] + " 执行。" );

    主要文件
    运行了shell命令
    node support.js 2
    这里使用了参数

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

spawn

  • 特点: 命令与参数分开放?

  • 用法

    1
    child_process.spawn(command[, args][, options])
    • args为Array字符串参数数组
    • options配置一些环境变量,子进程stdio配置,uig,gid等
    • 返回值是stdout与stderr
  • 举例
    注意代码中虽然是阻塞风格的写法,没有用回调函数,但实际运行可能是异步的
    这里更像是将shell命令与其参数分开放而已

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

fork

  • 特点: 相当于固定了shell命令必须是node,可以在父进程和子进程之间建立进程通信

  • 用法

    1
    child_process.fork(modulePath[, args][, options])
    • modulePath表示运行的模块,即文件名
    • args中定义命令的参数
    • options中与上面类似
      • silent,子进程的stdin,stdout,stderr是否会被关联至父进程,默认为false
  • 举例

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

JXCore打包

JXCore是nodejs的一个多线程强化版
有一个小功能是打包,编译
假设某项目有文件如下

1
2
3
4
5
6
images/
index.htm
index.js
node_modules/
scripts/
style/

打包,并指定index.js为该项目的主文件

1
jx emacs的use-package宏.org index.js index

会生成两个文件

  • index.jxp 中间文件,包含项目信息
  • index.jx 二进制文件,可以运行

效果

  • nodejs以脚本运行需要:

    1
    node index.js arguments
  • jx以程序运行

    1
    jx index.jx arguments

访问MySQL

需要mysql包
核心是使用mysql.createConnection获取连接
然后使用connection对象的query方法完成功能
而查询结果的输出靠的是nodejs特色的回调函数

举例

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

连接参数

连接时的参数也比较重要
不过都是与mysql有关的

参数 描述
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的配置文件

普通操作

  1. Read

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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');
    });
  2. Create

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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');
    });
  3. Update

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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');
    });
  4. Delete

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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');
    });

访问MongoDB

需要mongodb包

创建连接

这里在回调函数中返回了连接对象db
连接后立马关闭了

1
2
3
4
5
6
7
8
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/runoob";

MongoClient.connect(url, function(err, db) {
if (err) throw err;
console.log("数据库已创建!");
db.close();
});

数据库操作

  1. 创建集合(类似于表)

    其他代码略
    需要使用连接对象db创建使用的数据库的对象dbase
    再使用dbase对象来完成其他所有操作

    1
    2
    3
    4
    5
    6
    var dbase = db.db("runoob");
    dbase.createCollection('site', function (err, res) {
    if (err) throw err;
    console.log("创建集合!");
    db.close();
    });
  2. Cerate

    这里dbo是数据库对象
    collection方法使用集合名寻址集合,
    然后使用集合对象的insertOne方法插入数据
    回调函数中做错误处理

    1
    2
    3
    4
    5
    6
    var myobj = { name: "菜鸟教程", url: "www.runoob" };
    dbo.collection("site").insertOne(myobj, function(err, res) {
    if (err) throw err;
    console.log("文档插入成功");
    db.close();
    });
  3. Read

    find()方法中的字符是查询语句,
    这里查询所有数据.
    使用find()方法后就得到查询结果的对象.
    toArray函数也是有回调方法的!

    1
    2
    3
    4
    5
    dbo.collection("site").find({}).toArray(function(err, result) { // 返回集合中所有数据
    if (err) throw err;
    console.log(result);
    db.close();
    });

    或者指定条件
    条件是键值对,这是MongoDB的特色

    1
    2
    3
    4
    5
    6
    var whereStr = {"name":'菜鸟教程'};  // 查询条件
    dbo.collection("site").find(whereStr).toArray(function(err, result) {
    if (err) throw err;
    console.log(result);
    db.close();
    });
  4. Update

    MongoDB特色是可以在方法上指定更新一条还是多条
    单条使用collection对象的updateOne方法

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

    多条使用collection对象的updateMany方法

    1
    2
    3
    4
    5
    6
    7
    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();
    });
  5. Delete

    都是collection对象的deleteMany方法
    代表检索条件的参数使用键值对定义

    1
    2
    3
    4
    5
    6
    var whereStr = { type: "en" };  // 查询条件
    dbo.collection("site").deleteMany(whereStr, function(err, obj) {
    if (err) throw err;
    console.log(obj.result.n + " 条文档被删除");
    db.close();
    });
  6. 排序

    使用查询结果对象的sort方法,在参数中指定某个字段是升序还是降序
    代表排序条件的参数使用键值对定义

    这里按照type字段的升序排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var MongoClient = require('mongodb').MongoClient;
    var url = "mongodb://localhost:27017/";

    MongoClient.connect(url, 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();
    });
    });
  7. 查询分页

    使用查询结果的limit(N)方法限定仅仅返回N条

  8. 左连接

    MongoDB不是关系型数据库,也就没有连接的概念,
    但在数据的最后表现形式上做出一样的效果
    比如集合orders

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

    集合products

    1
    2
    3
    4
    5
    [
    { _id: 154, name: '笔记本电脑' },
    { _id: 155, name: '耳机' },
    { _id: 156, name: '台式电脑' }
    ]

    两个集合本身没有任何关系
    需要人为在程序中处理使得两个集合相互关联

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    var MongoClient = require('mongodb').MongoClient;
    var url = "mongodb://127.0.0.1:27017/";

    MongoClient.connect(url, 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();
    });
    });
  9. 删除集合

    还是collection对象的drop方法

    1
    2
    3
    4
    5
    dbo.collection("test").drop(function(err, delOK) {  // 执行成功 delOK 返回 true,否则返回 false
    if (err) throw err;
    if (delOK) console.log("集合已删除");
    db.close();
    });