Node.js 学习笔记
Hello world
console.log('Hello world');
console.log与c语言中的printf功能类似,也接受任意多个参数,支持%d,%s变量引用:
console.log('%s:%d','Hello',25);
Hello:25
运行无参数的 node 将会启动一个 JavaScript 的交互式 shell
$ node
> console.log('Hello World');
Hello World
undefined
在任何时候,连续按两次 Ctrl + C 即可退出Node.js 的 REPL 模式。
建立HTTP服务器
首先创建一个名为app.js的文件:
//app.js
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Node.js</h1>');
res.end('<p>Hello World</p>');
}).listen(3000);
console.log("HTTP server is listening at port 3000.");
接下来运行 node app.js命令,打开浏览器访问 http://127.0.0.1:3000
Node.js
Hello World
这个程序调用了 Node.js 提供的 http 模块,对所有 HTTP 请求答复同样的内容并监听 3000 端口.
Node.js 教程
Node.js
Node.js 是能够在伺服器端运行 JavaScript 的开放原始码、跨平台 JavaScript 执行环境。
Node.js 由 Node.js 基金会持有和维护 ,并与 Linux 基金会有合作关系 。
Node.js 采用 Google 开发的 V8 执行程式码,使用事件驱动、非阻塞和非同步输入输出模型等技术来提高效能,可优化应用程式的传输量和规模。
这些技术通常用于资料密集的即时应用程式。
一句话概括就是:JS 非常牛 X!
我个人的理解比较简单:PHP 是 WEB 开发的过去式,Node.js 是 WEB 开发的将来时!
一切能用 JS 来做的事情,最终都会用 JS 来做!
尽力推动 JS 生态,包括并不限网页开发,Chrome 扩展开发,PC 桌面应用的开发,手机端应用的开发,服务器任务批处理脚本等等;
Node.js 对前端生态的推动有里程碑的意义
Node.js 大部分基本模组都用 JavaScript 语言编写。
在 Node.js 出现之前,JavaScript 通常作为用户端程式设计语言使用,以 JavaScript 写出的程式常在用户的浏览器上执行。
Node.js 的出现使 JavaScript 也能用于伺服器端编程。
Node.js 含有一系列内置模组,使得程式可以脱离 Apache HTTP Server 或 IIS,作为独立伺服器执行。
使用的版本
我们可以使用以下命令来查看当前的 Node 版本:
1 | 1. node -v |
注意:不同版本间可能是有差异的。
Hello World**!**
脚本模式
以下是我们的第一个 Node.js 程序:
1 | 1. console.log("Hello World"); |
保存该文件,文件名为 helloworld.js, 并通过 node 命令来执行:
1 | 1. node helloworld.js |
程序执行后,正常的话,就会在终端输出 Hello World。
交互模式
打开终端,键入 node 进入命令交互模式,可以输入一条代码语句后立即执行并显示结果,例如:
1 | 1. $ node> console.log('Hello World!');Hello World! |
执行绪
Node.js 以单执行绪执行,使用非阻塞 I/O 呼叫,这样既可以支援数以万计的并行连线,又不会因多执行绪本身的特点而带来麻烦。
众多请求只使用单执行绪的设计意味著可以用于建立高并行应用程式。Node.js 应用程式的设计目标是任何需要操作 I/O 的函式都使用回呼函式。
这种设计的缺点是,如果不使用 cluster、StrongLoop Process Manager 或 pm2 等模组,Node.js 就难以处理多核或多执行绪等情况。
V8
主条目:V8 (JavaScript 引擎)
V8 是为 Google Chrome 设计的 JavaScript 运行引擎,Google 于 2008 年将其开源。V8 用 C++ 写成,它将 JavaScript 源代码编译成本地机器码而不是解释执行。
Node.js 用 libuv 来处理异步事件,而 V8 提供了 JavaScript 的实时运行环境。
libuv 是一个网络和文件系统功能的抽象层,既可以用于 Windows 又可以用于符合 POSIX 标准的系统,例如 Linux、OS X 和 Unix。
Node.js 的核心功能被包含进一个 JavaScript 库,并通过 C++ 将各部分与操作系统进行联系。
npm
主条目:node 包管理器
npm 是 Node.js 附带的包管理器。
npm 是一个命令行工具,用于从 NPM Registry 中下载、安装 Node.js 程序,同时解决依赖问题。
npm 提高了开发的速度,因为它能够负责第三方 Node.js 程序的安装与管理。
统一 API
Node.js 将浏览器、数据(例如 MongoDB 或 CouchDB)等组合到一起,通过 JSON 提供一个统一的接口。
由于前端框架和一些基本的后端开发技术(如 MVC、MVP、MVVM 等)变得流行,
Node.js 也支持客户端和服务器端重新利用相同的模型和接口。
事件循环
Node.js 将其注册到操作系统中,这样可以及时注意到新连接的产生。当新连接产生时,操作系统会产生一个回调。
在 Node.js 运行时内部,每个连接都被分配一个小型的堆。
与其他服务器程序不同的是,Node.js 不使用进程或线程处理连接,而是采用事件循环来处理并发连接。
而且 Node.js 的事件循环不需要手动调用。
在回调函数定义之后,服务器进入事件循环。
当回调函数均被执行完毕之后,Node.js 结束事件循环。
开发工具
桌面 IDE
- Atom(免费、开源软件)
- Brackets(免费、开源软件)
- Sublime Text(商业软件)
- JetBrains IntelliJ IDEA(商业软件)
- JetBrains WebStorm**(商业软件,五星推荐)**
- Microsoft Visual Studio with Node.js Tools for Visual Studio(商业软件)
- Microsoft Visual Studio with TypeScript(商业软件)
- Nodeclipse Enide Studio(免费、开源软件、在 Eclipse 基础上开发)
- NoFlo –与 GNOME API 集成的流式开发环境
- Visual Studio Code(免费、开源软件)
在线编辑器
- Koding
- Codenvy IDE
- Cloud9 IDE
- Codiad
运行时和调试器
- Visual Studio Code (五星推荐)
- Microsoft Visual Studio(商业)with Node.js Tools for Visual Studio(免费)
- Microsoft WebMatrix(免费、闭源软件)
框架
- 服务器端框架:Express.js, Socket.IO, Koa.js, Hapi.js, Total.js
- MVC 框架:Meteor, Derby, Sails, Mean, MeanJS, Tower.js, Nombo, Geddy, Compound, Yahoo! Mojito
下载
Node.js 下载地址
https://nodejs.org/en/download/
Node.js 历史版本
Node.js 历史版本下载地址:https://nodejs.org/dist/
这里是 Nodejs 历史版本大全
Windows Node.js 安装
Node.js 安装配置
本章节我们将向大家介绍在 Windows 上安装 Node.js 的方法。
Node.js 安装包及源码下载地址为:https://nodejs.org/en/download/。
你可以根据不同平台系统选择你需要的 Node.js 安装包。
Windows 上安装 Node.js
你可以采用以下两种方式来安装。
1**、**Windows 安装包 (.msi)
本文实例以 v10.xx.xx 版本为例,其他版本类似, 安装步骤:
步骤 1 : 双击下载后的安装包 v10.xx.xx**,如下所示:**
步骤 2 : 点击以上的 Run**(运行),将出现如下界面:**
步骤 3 : 勾选接受协议选项,点击 next**(下一步) 按钮 :**
步骤 4 : 选择 Node.js 安装目录
默认安装目录为 “C:\Program Files\nodejs\” , 你可以修改目录,并点击 next(下一步):
非常不推荐用户修改安装目录!!!使用默认的就好
步骤 5 : 点击树形图标来选择你需要的安装模式 , 然后点击下一步 next**(下一步)**
步骤 6 : 点击 Install**(安装) 开始安装 Node.js**。你也可以点击 Back**(返回)来修改先前的配置。 然后并点击 next**(下一步):
安装过程:
点击 Finish(完成)按钮退出安装向导。
检测 PATH 环境变量是否配置了 Node.js,点击开始 =》运行 =》输入”cmd” => 输入命令”path”,输出如下结果:
1 | 1. PATH=C:\WINDOWS\system32; |
检查 Node.js 版本
1 | 1. node -v |
2**、**Windows 二进制文件 (.exe)
使用 .exe 的安装包进行安装
安装步骤
步骤 1 : 双击下载的安装包 Node.exe ,将出现如下界面 :
点击 Run(运行)按钮将出现命令行窗口:
测试 Node.js 是否安装成功
进入 node.exe 所在的目录,
输出结果,说明你已经成功安装了 Node.js。
Node.js 创建第一个应用
使用 Node.js 时做一个 WEB 服务的时候,我们不仅仅 在实现一个应用,同时还实现了整个 HTTP 服务器。
事实上,我们的 Web 应用 以及对应的 Web 服务器 基本上是一样的。
在我们创建 Node.js 第一个 “Hello, World!” 应用前,让我们先了解下 Node.js 应用是由哪几部分组成的:
- 引入 required 模块:
我们可以使用require
指令来载入 Node.js 模块。
- 创建服务器:
服务器可以监听客户端的请求,类似于 Apache 、Nginx 等 HTTP 服务器。
- 接收请求与响应请求 服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。
创建 Node.js 应用
步骤一、引入 required 模块
我们使用 require 指令来载入 http 模块,并将实例化的 HTTP 赋值给变量 http,实例如下:
\1. var http = require(“http”);
步骤二、创建服务器
接下来我们使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定 8888 端口。
函数通过 request, response 参数来接收和响应数据。
实例如下,在你项目的根目录下创建一个叫 server.js 的文件,并写入以下代码:
1 | 1. var http = require('http'); |
使用 node 命令执行以上的代码:
1 | 1. node server.js |
接下来,打开浏览器访问 http://127.0.0.1:8888/,你会看到一个写着 “Hello World”的网页。
分析 Node.js 的 HTTP 服务器:
第一行请求(require)Node.js 自带的 http 模块,并且把它赋值给 http 变量。
接下来我们调用 http 模块提供的函数: createServer 。
- 这个函数会返回 一个对象,这个对象有一个叫做 listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号。
NPM 使用介绍
NPM 使用介绍
NPM 是随同 NodeJS 一起安装的包管理工具,能解决 NodeJS 代码部署上的很多问题,常见的使用场景有以下几种:
- 允许用户从 NPM 服务器下载别人编写的第三方包到本地使用。
- 允许用户从 NPM 服务器下载并安装别人编写的命令行程序到本地使用。
- 允许用户将自己编写的包或命令行程序上传到 NPM 服务器供别人使用。
由于新版的 nodejs 已经集成了 npm,所以之前 npm 也一并安装好了。
同样可以通过输入 “npm -v” 来测试是否成功安装。命令如下,出现版本提示表示安装成功:
1 | 1. $ npm -v6.4.1 |
如果你安装的是旧版本的 npm,可以很容易得通过 npm 命令来升级,命令如下:
1 | 1. $ sudo npm install npm -g |
如果是 Window 系统使用以下命令即可:
1 | 1. npm install npm -g |
使用淘宝镜像
1 | 1. npm install -g cnpm --registry=https://registry.npm.taobao.org |
使用 npm 命令安装模块
npm 安装 Node.js 模块语法格式如下:
1 | 1. $ npm install |
以下实例,我们使用 npm 命令安装常用的 Node.js web 框架模块 express:
1 | 1. $ npm install express |
安装好之后,express 包就放在了工程目录下的 node_modules 目录中,因此在代码中只需要通过 require(**‘express’)** 的方式就好,无需指定第三方包路径。
1 | 1. var express = require('express'); |
全局安装与本地安装
npm 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有 -g 而已,比如
1 | 1. npm install express # 本地安装npm install express -g # 全局安装 |
如果出现以下错误:
1 | 1. npm err! Error: connect ECONNREFUSED 127.0.0.1:8087 |
解决办法为:
1 | 1. $ npm config set proxy null |
本地安装
- 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录)
- 如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
- 可以通过 require() 来引入本地安装的包。
全局安装
- 将安装包放在 /usr/local 下或者你 node 的安装目录。
- 可以直接在命令行里使用。
如果你希望具备两者功能,则需要在两个地方安装它或使用 npm link 。
接下来我们使用全局方式安装 express
1 | 1. $ npm install express -g |
安装过程输出如下内容,第一行输出了模块的版本号及安装位置。
1 | 1. express@4.13.3 node_modules/express |
查看安装信息
你可以使用 npm ls 命令来查看所有全局安装的模块:
1 | 1. $ npm ls -g |
如果要查看某个模块的版本号,可以使用命令如下:
1 | 1. $ npm ls grunt |
使用 package.json
package.json 位于模块的目录下,用于定义包的属性。
接下来让我们来看下 express 包的 package.json 文件,位于 node_modules/express/package.json 内容:
1 | 1. { |
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 - 关键字
卸载模块
我们可以使用 npm uninstall 来卸载 Node.js 模块。
1 | 1. $ npm uninstall express |
卸载后,你可以到 /node_modules/ 目录下查看包是否还存在,或者使用以下命令查看:
1 | 1. $ npm ls |
更新模块
我们可以使用 npm update更新模块:
1 | 1. $ npm update express |
搜索模块
使用 npm search来搜索模块:
1 | 1. $ npm search express |
创建模块
创建模块,package.json 文件是必不可少的。
我们可以使用 NPM 生成 package.json 文件,生成的文件包含了基本的结果。
1 | 1. $ npm init |
以上的信息,你需要根据你自己的情况输入。在最后输入 “yes” 后会生成 package.json 文件。
接下来我们可以使用 npm adduser 在 npm 资源库中注册用户(使用邮箱注册):
1 | 1. $ npm adduserUsername: mcmohdPassword:Email: (this IS public) mcmohd@gmail.com |
接下来我们就用 npm publishl来发布模块:
1 | 1. $ npm publish |
如果你以上的步骤都操作正确,你就可以跟其他模块一样使用 npm 来安装。
版本号
使用 NPM 下载和发布代码时都会接触到版本号。
NPM 使用语义版本号来管理代码,这里简单介绍一下。
语义版本号分为 X.Y.Z 三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。
- 如果只是修复 bug,需要更新 Z 位。
- 如果是新增了功能,但是向下兼容,需要更新 Y 位。
- 如果有大变动,向下不兼容,需要更新 X 位。
版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。
例如”argv”: “0.0.x”表示依赖于 0.0.x 系列的最新版 argv。
NPM 常用命令
NPM 还提供了很多功能,package.json 里也有很多其它有用的字段。
介绍一些NPM常用命令。
NPM 提供了很多命令,例如 install 和 publish,使用 npm help 可查看所有命令。
- 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 镜像
国内直接使用 npm 的官方镜像,如果网络不怎么给力是非常慢的,这里推荐使用淘宝 NPM 镜像。
淘宝 NPM 镜像是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10 分钟 一次以保证尽量与官方服务同步。
你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:
1 | 1. $ npm install -g cnpm --registry=https://registry.npm.taobao.org |
这样就可以使用 cnpm 命令来安装模块了:
1 | 1. $ cnpm install [name] |
更多信息可以查阅:http://npm.taobao.org/。
nrm 管理镜像
更加推荐使用 nrm 来控制 npm 的源
他可以一条命令切到淘宝 / 网易 /XXX 的源,也可以一条命令切回官方源头,这让我觉得特别给力,特别是我需要发我自己制作的 npm 包的时候;
Node.js REPL**(交互式解释器)**
Node.js REPL**(交互式解释器)**
Node.js REPL(Read Eval Print Loop: 交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应。
Node 自带了交互式解释器,可以执行以下任务:
- 读取(Read) - 读取用户输入,解析输入了 Javascript 数据结构并存储在内存中。
- 执行(Eval) - 执行输入的数据结构
- 打印(Print) - 输出结果
- 循环(Loop) - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。
Node 的交互式解释器可以很好的调试 Javascript 代码。
开始学习 REPL
我们可以输入以下命令来启动 Node 的终端:
1 | 1. $ node> |
这时我们就可以在 > 后输入简单的表达式,并按下回车键来计算结果。
简单的表达式运算
接下来让我们在 Node.js REPL 的命令行窗口中执行简单的数学运算:
1 | 1. $ node |
使用变量
你可以将数据存储在变量中,并在你需要的时候使用它。
变量声明需要使用 var 关键字,如果没有使用 var 关键字变量会直接打印出来。
使用 var 关键字的变量可以使用 console.log() 来输出变量。
1 | 1. $ node |
多行表达式
Node REPL 支持输入多行表达式,这就有点类似 JavaScript。接下来让我们来执行一个 do-while 循环:
1 | 1. $ node |
… 三个点的符号是系统自动生成的,你回车换行后即可。Node 会自动检测是否为连续的表达式。
下划线 (_) 变量
你可以使用下划线 (_) 获取上一个表达式的运算结果:
1 | 1. $ node |
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 | 1. $ node>(^C again to quit)> |
Node.js 回调函数
Node.js 回调函数
Node.js 异步编程的直接体现就是回调。
异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。
回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。
例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。
回调函数一般作为函数的最后一个参数出现:
1 | 1. function foo1(name, age, callback) { } |
阻塞代码实例
创建一个文件 input.txt ,内容如下:
- 我的博客地址:www.mrschen.top
创建 main.js 文件,代码如下:
1 | 1. var fs = require("fs"); |
以上代码执行结果如下:
1 | 1. $ node main.js |
非阻塞代码实例
创建一个文件 input.txt ,内容如下:
1 | 1. 我的博客地址:www.mrschen.top |
创建 main.js 文件,代码如下:
1 | 1. var fs = require("fs"); |
以上代码执行结果如下:
1 | 1. $ node main.js |
以上两个实例我们了解了阻塞与非阻塞调用的不同。第一个实例在文件读取完后才执行完程序。 第二个实例我们不需要等待文件读取完,这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。
因此,阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。
Node.js 事件循环
Node.js 事件循环
Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。
Node.js 几乎每一个 API 都是支持回调函数的。
Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
Node.js 单线程类似进入一个 while(true) 的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数。
事件驱动程序
Node.js 使用事件驱动模型,当 web server 接收到请求,就把它关闭然后进行处理,然后去服务下一个 web 请求。
当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
这个模型非常高效可扩展性非常强,因为 webserver 一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式 IO 或者事件驱动 IO)
在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。
整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题 (Subject),而所有注册到这个事件上的处理函数相当于观察者 (Observer)。
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:
1 | 1. // 引入 events 模块 |
以下程序绑定事件处理程序:
1 | 1. // 绑定事件及事件的处理程序 |
我们可以通过程序触发事件:
1 | 1. // 触发事件eventEmitter.emit('eventName'); |
实例
创建 main.js 文件,代码如下所示:
1 | 1. // 引入 events 模块 |
接下来让我们执行以上代码:
1 | 1. $ node main.js |
Node 应用程序是如何工作的?
在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。
接下来让我们来重新看下前面的实例,创建一个 input.txt , 文件内容如下:
1 | \1. 我的博客地址:www.mrschen.top |
以上程序中 fs.readFile() 是异步函数用于读取文件。 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。
如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。
执行以上代码,执行结果如下:
1 | 1. 我的博客地址:www.mrschen.top |
接下来我们删除 input.txt 文件,执行结果如下所示:
1 | 1. 程序执行完毕Error: ENOENT, open 'input.txt' |
因为文件 input.txt 不存在,所以输出了错误信息。
Node.js EventEmitter
Node.js EventEmitter
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。
EventEmitter 类
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
你可以通过 require(“events”); 来访问该模块。
1 | 1. // 引入 events 模块 |
EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。
下面我们用一个简单的例子说明 EventEmitter 的用法:
1 | 1. //event.js 文件 |
执行结果如下:
运行这段代码,1 秒后控制台输出了 ‘some_event 事件触发’。其原理是 event 对象注册了事件 some_event 的一个监听器,然后我们通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件 some_event,此时会调用 some_event 的监听器。
1 | 1. $ node event.js |
EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
让我们以下面的例子解释这个过程:
1 | 1. //event.js 文件 |
执行以上代码,运行的结果如下:
1 | 1. $ node event.js |
以上例子中,emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件。
运行结果中可以看到两个事件监听器回调函数被先后调用。 这就是 EventEmitter 最简单的用法。
EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件。接下来我们来具体看下 EventEmitter 的属性介绍。
方法
序号 | 方法 & 描述 |
---|---|
1 | addListener(event, listener) 为指定事件添加一个监听器到监听器数组的尾部。 |
2 | on(event, listener) 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。server.on(‘connection’, function (stream) { console.log(‘someone connected!’);}); |
3 | once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。server.once(‘connection’, function (stream) { console.log(‘Ah, we have our first user!’);}); |
4 | removeListener(event, listener) 移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。var callback = function(stream) { console.log(‘someone connected!’);};server.on(‘connection’, callback);// …server.removeListener(‘connection’, callback); |
5 | removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。 |
6 | setMaxListeners(n) 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。 |
7 | listeners(event) 返回指定事件的监听器数组。 |
8 | emit(event, [arg1], [arg2], […]) 按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。 |
类方法
序号 | 方法 & 描述 |
---|---|
1 | listenerCount(emitter, event) 返回指定事件的监听器数量。 |
\1. events.EventEmitter.listenerCount(emitter, eventName) //已废弃,不推荐events.emitter.listenerCount(eventName) //推荐
事件
序号 | 事件 & 描述 |
---|---|
1 | newListener event - 字符串,事件名称listener - 处理事件函数该事件在添加新监听器时被触发。 |
2 | removeListener event - 字符串,事件名称listener - 处理事件函数从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。 |
实例
以下实例通过 connection(连接)事件演示了 EventEmitter 类的应用。
创建 main.js 文件,代码如下:
1 | 1.var events = require('events'); |
以上代码,执行结果如下所示:
1 | 1. $ node main.js |
error 事件
EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。
当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。
我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:
1 | 1. var events = require('events'); |
运行时会显示以下错误:
1 | 1. node.js:201 |
继承 EventEmitter
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发生应该是一个对象的方法。
其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。
Node.js Buffer
Node.js Buffer**(缓冲区)**
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像 TCP 流或文件流时,必须使用到二进制数据。因此在 Node.js 中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理 I/O 操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。
在 v6.0 之前创建 Buffer 对象直接使用 new Buffer() 构造函数来创建对象实例,但是 Buffer 对内存的权限操作相比很大,可以直接捕获一些敏感信息,所以在 v6.0 以后,官方文档里面建议使用 Buffer.from() 接口去创建 Buffer 对象。
Buffer 与字符编码
Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。
1 | 1. const buf = Buffer.from('axihe', 'ascii'); |
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 | i. // 创建一个长度为 10、且用 0 填充的 Buffer。 |
写入缓冲区
语法
写入 Node 缓冲区的语法如下所示:
1 | 1. buf.write(string[, offset[, length]][, encoding]) |
参数
参数描述如下:
- string - 写入缓冲区的字符串。
- offset - 缓冲区开始写入的索引值,默认为 0 。
- length - 写入的字节数,默认为 buffer.length
- encoding - 使用的编码。默认为 ‘utf8’ 。
根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。 如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。
返回值
返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。
实例
1 | 1. buf = Buffer.alloc(256); |
执行以上代码,输出结果为:
1 | 1. $node main.js |
从缓冲区读取数据
语法
读取 Node 缓冲区数据的语法如下所示:
1 | 1. buf.toString([encoding[, start[, end]]]) |
参数
参数描述如下:
- encoding - 使用的编码。默认为 ‘utf8’ 。
- start - 指定开始读取的索引位置,默认为 0。
- end - 结束位置,默认为缓冲区的末尾。
返回值
解码缓冲区数据并使用指定的编码返回字符串。
实例
1 | 1. buf = Buffer.alloc(26); |
执行以上代码,输出结果为:
1 | 1. $ node main.js |
将 Buffer 转换为 JSON 对象
语法
将 Node Buffer 转换为 JSON 对象的函数语法格式如下:
1 | 1. buf.toJSON() |
当字符串化一个 Buffer 实例时,JSON.stringify()会隐式地调用该 toJSON()。
返回值
返回 JSON 对象。
实例
1 | 1. const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); |
缓冲区合并
语法
Node 缓冲区合并的语法如下所示:
1 | 1. Buffer.concat(list[, totalLength]) |
参数
参数描述如下:
- list - 用于合并的 Buffer 对象数组列表。
- totalLength - 指定合并后 Buffer 对象的总长度。
返回值
返回一个多个成员合并的新 Buffer 对象。
实例
1 | 1. var buffer1 = Buffer.from(('node')); |
缓冲区比较
语法
Node Buffer 比较的函数语法如下所示,该方法在 Node.js v0.12.2 版本引入:
1 | 1. buf.compare(otherBuffer); |
参数
参数描述如下:
- otherBuffer - 与 buf 对象比较的另外一个 Buffer 对象。
返回值
返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。
实例
1 | 1. var buffer1 = Buffer.from('ABC'); |
拷贝缓冲区
语法
Node 缓冲区拷贝语法如下所示:
1 | 1. buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]) |
参数
参数描述如下:
- targetBuffer - 要拷贝的 Buffer 对象。
- targetStart - 数字,可选,默认:0
- sourceStart - 数字,可选,默认:0
- sourceEnd - 数字,可选,默认:buffer.length
返回值
没有返回值。
实例
1 | 1. var buf1 = Buffer.from('abcdefghijkl'); |
缓冲区裁剪
Node 缓冲区裁剪语法如下所示:
1 | 1. buf.slice([start[, end]]) |
参数
参数描述如下:
- start - 数字,可选,默认:0
- end - 数字,可选,默认:buffer.length
返回值
返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 start 到 end 的位置剪切。
实例
1 | 1. var buffer1 = Buffer.from('axihe'); |
缓冲区长度
语法
Node 缓冲区长度计算语法如下所示:
1 | 1. buf.length; |
返回值
返回 Buffer 对象所占据的内存长度。
实例
1 | 1. var buffer = Buffer.from('www.axihe.com'); |
方法参考手册
以下列出了 Node.js Buffer 模块常用的方法(注意有些方法在旧版本是没有的):
序号 | 方法 & 描述 |
---|---|
1 | new Buffer(size) 分配一个新的 size 大小单位为8位字节的 buffer。 注意, size 必须小于 kMaxLength,否则,将会抛出异常 RangeError。废弃的: 使用 Buffer.alloc() 代替(或 Buffer.allocUnsafe())。 |
2 | new Buffer(buffer) 拷贝参数 buffer 的数据到 Buffer 实例。废弃的: 使用 Buffer.from(buffer) 代替。 |
3 | new Buffer(str[, encoding]) 分配一个新的 buffer ,其中包含着传入的 str 字符串。 encoding 编码方式默认为 ‘utf8’。 废弃的: 使用 Buffer.from(string[, encoding]) 代替。 |
4 | buf.length 返回这个 buffer 的 bytes 数。注意这未必是 buffer 里面内容的大小。length 是 buffer 对象所分配的内存数,它不会随着这个 buffer 对象内容的改变而改变。 |
5 | buf.write(string[, offset[, length]][, encoding]) 根据参数 offset 偏移量和指定的 encoding 编码方式,将参数 string 数据写入buffer。 offset 偏移量默认值是 0, encoding 编码方式默认是 utf8。 length 长度是将要写入的字符串的 bytes 大小。 返回 number 类型,表示写入了多少 8 位字节流。如果 buffer 没有足够的空间来放整个 string,它将只会只写入部分字符串。 length 默认是 buffer.length - offset。 这个方法不会出现写入部分字符。 |
6 | buf.writeUIntLE(value, offset, byteLength[, noAssert]) 将 value 写入到 buffer 里, 它由 offset 和 byteLength 决定,最高支持 48 位无符号整数,小端对齐,例如: const buf = Buffer.allocUnsafe(6); buf.writeUIntLE(0x1234567890ab, 0, 6); // 输出: |
7 | buf.writeUIntBE(value, offset, byteLength[, noAssert]) 将 value 写入到 buffer 里, 它由 offset 和 byteLength 决定,最高支持 48 位无符号整数,大端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。const buf = Buffer.allocUnsafe(6); buf.writeUIntBE(0x1234567890ab, 0, 6); // 输出: |
8 | buf.writeIntLE(value, offset, byteLength[, noAssert]) 将value 写入到 buffer 里, 它由offset 和 byteLength 决定,最高支持48位有符号整数,小端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。 |
9 | buf.writeIntBE(value, offset, byteLength[, noAssert]) 将value 写入到 buffer 里, 它由offset 和 byteLength 决定,最高支持48位有符号整数,大端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。 |
10 | buf.readUIntLE(offset, byteLength[, noAssert]) 支持读取 48 位以下的无符号数字,小端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。 |
11 | buf.readUIntBE(offset, byteLength[, noAssert]) 支持读取 48 位以下的无符号数字,大端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。 |
12 | buf.readIntLE(offset, byteLength[, noAssert]) 支持读取 48 位以下的有符号数字,小端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。 |
13 | buf.readIntBE(offset, byteLength[, noAssert]) 支持读取 48 位以下的有符号数字,大端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。 |
14 | buf.toString([encoding[, start[, end]]]) 根据 encoding 参数(默认是 ‘utf8’)返回一个解码过的 string 类型。还会根据传入的参数 start (默认是 0) 和 end (默认是 buffer.length)作为取值范围。 |
15 | buf.toJSON() 将 Buffer 实例转换为 JSON 对象。 |
16 | buf[index] 获取或设置指定的字节。返回值代表一个字节,所以返回值的合法范围是十六进制0x00到0xFF 或者十进制0至 255。 |
17 | buf.equals(otherBuffer) 比较两个缓冲区是否相等,如果是返回 true,否则返回 false。 |
18 | buf.compare(otherBuffer) 比较两个 Buffer 对象,返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。 |
19 | buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]) buffer 拷贝,源和目标可以相同。 targetStart 目标开始偏移和 sourceStart 源开始偏移默认都是 0。 sourceEnd 源结束位置偏移默认是源的长度 buffer.length 。 |
20 | buf.slice([start[, end]]) 剪切 Buffer 对象,根据 start(默认是 0 ) 和 end (默认是 buffer.length ) 偏移和裁剪了索引。 负的索引是从 buffer 尾部开始计算的。 |
21 | buf.readUInt8(offset[, noAssert]) 根据指定的偏移量,读取一个无符号 8 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 如果这样 offset 可能会超出buffer 的末尾。默认是 false。 |
22 | buf.readUInt16LE(offset[, noAssert]) 根据指定的偏移量,使用特殊的 endian 字节序格式读取一个无符号 16 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
23 | buf.readUInt16BE(offset[, noAssert]) 根据指定的偏移量,使用特殊的 endian 字节序格式读取一个无符号 16 位整数,大端对齐。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
24 | buf.readUInt32LE(offset[, noAssert]) 根据指定的偏移量,使用指定的 endian 字节序格式读取一个无符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
25 | buf.readUInt32BE(offset[, noAssert]) 根据指定的偏移量,使用指定的 endian 字节序格式读取一个无符号 32 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
26 | buf.readInt8(offset[, noAssert]) 根据指定的偏移量,读取一个有符号 8 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
27 | buf.readInt16LE(offset[, noAssert]) 根据指定的偏移量,使用特殊的 endian 格式读取一个 有符号 16 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
28 | buf.readInt16BE(offset[, noAssert]) 根据指定的偏移量,使用特殊的 endian 格式读取一个 有符号 16 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
29 | buf.readInt32LE(offset[, noAssert]) 根据指定的偏移量,使用指定的 endian 字节序格式读取一个有符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
30 | buf.readInt32BE(offset[, noAssert]) 根据指定的偏移量,使用指定的 endian 字节序格式读取一个有符号 32 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
31 | buf.readFloatLE(offset[, noAssert]) 根据指定的偏移量,使用指定的 endian 字节序格式读取一个 32 位双浮点数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
32 | buf.readFloatBE(offset[, noAssert]) 根据指定的偏移量,使用指定的 endian 字节序格式读取一个 32 位双浮点数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
33 | buf.readDoubleLE(offset[, noAssert]) 根据指定的偏移量,使用指定的 endian 字节序格式读取一个 64 位双精度数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
34 | buf.readDoubleBE(offset[, noAssert]) 根据指定的偏移量,使用指定的 endian 字节序格式读取一个 64 位双精度数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。 |
35 | buf.writeUInt8(value, offset[, noAssert]) 根据传入的 offset 偏移量将 value 写入 buffer。注意:value 必须是一个合法的无符号 8 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则不要使用。默认是 false。 |
36 | buf.writeUInt16LE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的无符号 16 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
37 | buf.writeUInt16BE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的无符号 16 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
38 | buf.writeUInt32LE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式 (LITTLE-ENDIAN: 小字节序)将 value 写入 buffer。注意:value 必须是一个合法的无符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
39 | buf.writeUInt32BE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式 (Big-Endian: 大字节序)将 value 写入 buffer。注意:value 必须是一个合法的有符号 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
40 | buf.writeInt8(value, offset[, noAssert]) offset=”” 偏移量将=”” value=”” 写入=”” buffer=”” 。注意:value=”” 必须是一个合法的=”” signed=”” 8=”” 位整数。=”” 若参数=”” noassert=”” 为=”” true=”” 将不会验证=”” 和=”” 偏移量参数。=”” 这意味着=”” 可能过大,或者=”” 可能会超出=”” 的末尾从而造成=”” 被丢弃。=”” 除非你对这个参数非常有把握,否则尽量不要使用。默认是=”” false。 |
41 | buf.writeInt16LE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false 。 |
42 | buf.writeInt16BE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false 。 |
43 | buf.writeInt32LE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
44 | buf.writeInt32BE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
45 | buf.writeFloatLE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer 。注意:当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
46 | buf.writeFloatBE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer 。注意:当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
47 | buf.writeDoubleLE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个有效的 64 位 double 类型的值。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
48 | buf.writeDoubleBE(value, offset[, noAssert]) 根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个有效的 64 位 double 类型的值。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。 |
49 | buf.fill(value, offset) 使用指定的 value 来填充这个 buffer。如果没有指定 offset (默认是 0) 并且 end (默认是 buffer.length) ,将会填充整个buffer。 |
Node.js Stream
Node.js Stream(**流)**
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。
Node.js,Stream 有四种流类型:
- Readable - 可读操作。
- Writable - 可写操作。
- Duplex - 可读可写操作.
- Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
- data - 当有数据可读时触发。
- end - 没有更多的数据可读时触发。
- error - 在接收和写入过程中发生错误时触发。
- finish - 所有数据已被写入到底层系统时触发。
介绍常用的流操作。
从流中读取数据
创建 input.txt 文件,内容如下:
1 | 1. 我的博客地址:www.mrschen.top |
写入流
创建 main.js 文件, 代码如下:
1 | 1. var fs = require("fs"); |
查看 output.txt 文件的内容:
1 | 1. $ cat output.txt 我的博客地址:www.mrschen.top |
管道流
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
如上面的图片所示,我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。
以下实例我们通过读取一个文件内容并将内容写入到另外一个文件中。
设置 input.txt 文件内容如下:
1 | 1. 我的博客地址:www.mrschen.top管道流操作实例 |
链式流
链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。
接下来我们就是用管道和链式来压缩和解压文件。
创建 compress.js 文件, 代码如下:
1 | 1. var fs = require("fs"); |
Node.js 模块系统
Node.js**模块系统**
为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。
模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。
创建模块
在 Node.js 中,创建一个模块非常简单,如下我们创建一个 main.js 文件,代码如下:
1 | 1. var hello = require('./hello'); |
以上实例中,代码 require(‘./hello’) 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)。
Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
接下来我们就来创建 hello.js 文件,代码如下:
1 | 1. exports.world = function() { |
在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require(‘./hello’) 加载这个模块,然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。
有时候我们只是想把一个对象封装到模块中,格式如下:
1 | 1. module.exports = function() { |
模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。
服务端的模块放在哪里
也许你已经注意到,我们已经在代码中使用了模块了。像这样:
1 | 1. var http = require("http"); |
Node.js 中自带了一个叫做 http 的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。
这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。
Node.js 的 require 方法中的文件查找策略如下:
由于 Node.js 中存在 4 类模块(原生模块和3种文件模块),尽管 require 方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示:
从文件模块缓存中加载
尽管原生模块与文件模块的优先级不同,但是都会优先从文件模块的缓存中加载已经存在的模块。
从原生模块加载
原生模块的优先级仅次于文件模块缓存的优先级。require 方法在解析文件名之后,优先检查模块是否在原生模块列表中。以http模块为例,尽管在目录下存在一个 http/http.js/http.node/http.json 文件,require(“http”) 都不会从这些文件中加载,而是从原生模块中加载。
原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。
从文件加载
当文件模块缓存中不存在,而且不是原生模块的时候,Node.js 会解析 require 方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。
require方法接受以下几种参数的传递:
- http、fs、path等,原生模块。
- ./mod或../mod,相对路径的文件模块。
- /pathtomodule/mod,绝对路径的文件模块。
- mod,非原生模块的文件模块。
在路径 Y 下执行 require(X) 语句执行顺序:
1 | \1. 1. 如果 X 是内置模块 |
exports 和 module.exports 的使用
如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(类似class,包含了很多属性和方法),就用 module.exports。
Node.js 函数
Node.js 函数
在JavaScript中,一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。
Node.js中函数的使用与Javascript类似,举例来说,你可以这样做:
1 | 1. function say(word) { |
以上代码中,我们把 say 函数作为execute函数的第一个变量进行了传递。这里传递的不是 say 的返回值,而是 say 本身!
这样一来, say 就变成了execute 中的本地变量 someFunction ,execute可以通过调用 someFunction() (带括号的形式)来使用 say 函数。
当然,因为 say 有一个变量, execute 在调用 someFunction 时可以传递这样一个变量。
匿名函数
我们可以把一个函数作为变量传递。但是我们不一定要绕这个”先定义,再传递”的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:
1 | 1. function execute(someFunction, value) { |
我们在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。
用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。
函数传递是如何让HTTP服务器工作的
带着这些知识,我们再来看看我们简约而不简单的HTTP服务器:
1 | 1. var http = require("http"); |
现在它看上去应该清晰了很多:我们向 createServer 函数传递了一个匿名函数。
用这样的代码也可以达到同样的目的:
1 | 1. var http = require("http"); |
Node.js 路由
Node.js 路由
我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。
因此,我们需要查看 HTTP 请求,从中提取出请求的 URL 以及 GET/POST 参数。这一功能应当属于路由还是服务器(甚至作为一个模块自身的功能)确实值得探讨,但这里暂定其为我们的 HTTP 服务器的功能。
我们需要的所有数据都会包含在 request 对象中,该对象作为 onRequest() 回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的 Node.JS 模块,它们分别是 url 和 querystring 模块。
1 | 1. url.parse(string).query |
当然我们也可以用 querystring 模块来解析 POST 请求体中的参数,稍后会有演示。
现在我们来给 onRequest() 函数加上一些逻辑,用来找出浏览器请求的 URL 路径:
server.js 文件代码:
1 | 1. var http = require("http"); |
好了,我们的应用现在可以通过请求的 URL 路径来区别不同请求了 – 这使我们得以使用路由(还未完成)来将请求以 URL 路径为基准映射到处理程序上。
在我们所要构建的应用中,这意味着来自 /start 和 /upload 的请求可以使用不同的代码来处理。稍后我们将看到这些内容是如何整合到一起的。
现在我们可以来编写路由了,建立一个名为 router.js 的文件,添加以下内容:
router.js 文件代码:
1 | 1. function route(pathname) { |
如你所见,这段代码什么也没干,不过对于现在来说这是应该的。在添加更多的逻辑以前,我们先来看看如何把路由和服务器整合起来。
我们的服务器应当知道路由的存在并加以有效利用。我们当然可以通过硬编码的方式将这一依赖项绑定到服务器上,但是其它语言的编程经验告诉我们这会是一件非常痛苦的事,因此我们将使用依赖注入的方式较松散地添加路由模块。
首先,我们来扩展一下服务器的 start() 函数,以便将路由函数作为参数传递过去,server.js 文件代码如下
server.js 文件代码:
1 | 1. var http = require("http"); |
同时,我们会相应扩展 index.js,使得路由函数可以被注入到服务器中:
index.js 文件代码:
1 | 1. var server = require("./server"); |
在这里,我们传递的函数依旧什么也没做。
如果现在启动应用(node index.js,始终记得这个命令行),随后请求一个 URL,你将会看到应用输出相应的信息,这表明我们的 HTTP 服务器已经在使用路由模块了,并会将请求的路径传递给路由:
\1. $ node index.jsServer has started.
以上输出已经去掉了比较烦人的 /favicon.ico 请求相关的部分。
浏览器访问 http://127.0.0.1:8888/,输出结果如下:
Node.js 常用工具
Node.js 常用工具
util 是一个 Node.js 核心模块,提供常用函数的集合,用于弥补核心 JavaScript 的功能 过于精简的不足。
util.inherits
util.inherits(constructor, superConstructor) 是一个实现对象间原型继承的函数。
JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同。JavaScript 没有提供对象继承的语言级别特性,而是通过原型复制来实现的。
在这里我们只介绍 util.inherits 的用法,示例如下:
1 | 1. var util = require('util'); |
我们定义了一个基础对象 Base 和一个继承自 Base 的 Sub,Base 有三个在构造函数内定义的属性和一个原型中定义的函数,通过 util.inherits 实现继承。运行结果如下:
1 | 1. base |
注意:Sub 仅仅继承了 Base 在原型中定义的函数,而构造函数内部创造的 base 属 性和 sayHello 函数都没有被 Sub 继承。
同时,在原型中定义的属性不会被 console.log 作 为对象的属性输出。如果我们去掉 objSub.sayHello(); 这行的注释,将会看到:
1 | 1. node.js:201throw e; // process.nextTick error, or 'error' event on first tick^TypeError: Object #<Sub> has no method 'sayHello'at Object.<anonymous> (/home/byvoid/utilinherits.js:29:8)at Module._compile (module.js:441:26)at Object..js (module.js:459:10)at Module.load (module.js:348:31)at Function._load (module.js:308:12)at Array.0 (module.js:479:10)at EventEmitter._tickCallback (node.js:192:40)node.js:201 |
util.inspect
util.inspect(object,[showHidden],[depth],[colors]) 是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象。
showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息。
depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多 少。如果不指定 depth,默认会递归 2 层,指定为 null 表示将不限递归层数完整遍历对象。 如果 color 值为 true,输出格式将会以 ANSI 颜色编码,通常用于在终端显示更漂亮 的效果。
特别要指出的是,util.inspect 并不会简单地直接把对象转换为字符串,即使该对 象定义了 toString 方法也不会调用。
1 | 1. var util = require('util'); |
运行结果是:
1 | 1. Person { name: 'byvoid', toString: [Function] } |
util.isArray(object)
如果给定的参数 “object” 是一个数组返回 true,否则返回 false。
1 | 1. var util = require('util'); |
util.isRegExp(object)
如果给定的参数 “object” 是一个正则表达式返回 true,否则返回 false。
1 | 1. var util = require('util'); |
util.isDate(object)
如果给定的参数 “object” 是一个日期返回 true,否则返回 false。
1 | 1. var util = require('util'); |
util.isError(object)
如果给定的参数 “object” 是一个错误对象返回 true,否则返回 false。
1 | 1. var util = require('util'); |
Node.js 工具模块
Node.js 工具模块
在 Node.js 模块库中有很多好用的模块。接下来我们为大家介绍几种常用模块的使用:
序号 | 模块名 & 描述 |
---|---|
1 | OS 模块 提供基本的系统操作函数。 |
2 | Path 模块 提供了处理和转换文件路径的工具。 |
3 | Net 模块 用于底层的网络通信。提供了服务端和客户端的的操作。 |
4 | DNS 模块 用于解析域名。 |
5 | Domain 模块 简化异步代码的异常处理,可以捕捉处理try catch无法捕捉的。 |
Node.js Web 模块
什么是 Web 服务器?
Web 服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,Web 服务器的基本功能就是提供 Web 信息浏览服务。它只需支持 HTTP 协议、HTML 文档格式及 URL,与客户端的网络浏览器配合。
大多数 web 服务器都支持服务端的脚本语言(php、python、ruby)等,并通过脚本语言从数据库获取数据,将结果返回给客户端浏览器。
目前最主流的三个 Web 服务器是 Apache、Nginx、IIS。
Web 应用架构
- Client - 客户端,一般指浏览器,浏览器可以通过 HTTP 协议向服务器请求数据。
- Server - 服务端,一般指 Web 服务器,可以接收客户端请求,并向客户端发送响应数据。
- Business - 业务层, 通过 Web 服务器处理应用程序,如与数据库交互,逻辑运算,调用外部程序等。
- Data - 数据层,一般由数据库组成。
使用 Node 创建 Web 服务器
Node.js 提供了 http 模块,http 模块主要用于搭建 HTTP 服务端和客户端,使用 HTTP 服务器或客户端功能必须调用 http 模块,代码如下:
1 | 1. var http = require('http'); |
以下是演示一个最基本的 HTTP 服务器架构(使用 8080 端口),创建 server.js 文件,代码如下所示:
实例
1 | 1. var http = require('http'); |
接下来我们在该目录下创建一个 index.html 文件,代码如下:
index.html 文件
1 | 1. <!DOCTYPE html> |
执行 server.js 文件:
1 | 1. $ node server.jsServer running at http://127.0.0.1:8080/ |
接着我们在浏览器中打开地址:http://127.0.0.1:8080/index.html,显示如下图所示:
执行 server.js 的控制台输出信息如下:
1 | 1. Server running at http://127.0.0.1:8080/ |
使用 Node 创建 Web 客户端
Node 创建 Web 客户端需要引入 http 模块,创建 client.js 文件,代码如下所示:
实例
1 | 1. var http = require('http'); |
新开一个终端,执行 client.js 文件,输出结果如下:
1 | 1. $ node client.js |
Node.js RESTful API
什么是 REST?
REST 即表述性状态传递(英文:Representational State Transfer,简称 REST)是 Roy Fielding 博士在 2000 年他的博士论文中提出来的一种软件架构风格。
表述性状态转移是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。需要注意的是,REST 是设计风格而不是标准。REST 通常基于使用 HTTP,URI,和 XML(标准通用标记语言下的一个子集)以及 HTML(标准通用标记语言下的一个应用)这些现有的广泛流行的协议和标准。REST 通常使用 JSON 数据格式。
HTTP 方法
以下为 REST 基本架构的四个方法:
- GET - 用于获取数据。
- PUT - 用于更新或添加数据。
- DELETE - 用于删除数据。
- POST - 用于添加数据。
RESTful Web Services
Web service 是一个平台独立的,低耦合的,自包含的、基于可编程的 web 的应用程序,可使用开放的 XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。
基于 REST 架构的 Web Services 即是 RESTful。
由于轻量级以及通过 HTTP 直接传输数据的特性,Web 服务的 RESTful 方法已经成为最常见的替代方法。可以使用各种语言(比如 Java 程序、Perl、Ruby、Python、PHP 和 Javascript\【包括 Ajax])实现客户端。
RESTful Web 服务通常可以通过自动客户端或代表用户的应用程序访问。但是,这种服务的简便性让用户能够与之直接交互,使用它们的 Web 浏览器构建一个 GET URL 并读取返回的内容。
创建 RESTful
首先,创建一个 json 数据资源文件 users.json,内容如下:
1 | 1. { |
基于以上数据,我们创建以下 RESTful API:
序号 | URI | HTTP 方法 | 发送内容 | 结果 |
---|---|---|---|---|
1 | listUsers | GET | 空 | 显示所有用户列表 |
2 | addUser | POST | JSON 字符串 | 添加新用户 |
3 | deleteUser | DELETE | JSON 字符串 | 删除用户 |
4 | :id | GET | 空 | 显示用户详细信息 |
获取用户列表:
以下代码,我们创建了 RESTful API listUsers,用于读取用户的信息列表, server.js 文件代码如下所示:
1 | 1. var express = require('express'); |
接下来执行以下命令:
1 | 1. $ node server.js |
在浏览器中访问 http://127.0.0.1:8081/listUsers,结果如下所示:
1 | 1. { |
添加用户
以下代码,我们创建了 RESTful API addUser, 用于添加新的用户数据,server.js 文件代码如下所示:
1 | 1. var express = require('express'); |
接下来执行以下命令:
1 | 1. $ node server.js应用实例,访问地址为 http://0.0.0.0:8081 |
在浏览器中访问 http://127.0.0.1:8081/addUser,结果如下所示:
1 | 1. { user1: |
显示用户详情
以下代码,我们创建了 RESTful API :id**(用户 id)**, 用于读取指定用户的详细信息,server.js 文件代码如下所示:
1 | 1. var express = require('express'); |
接下来执行以下命令:
1 | 1. $ node server.js应用实例,访问地址为 http://0.0.0.0:8081 |
在浏览器中访问 http://127.0.0.1:8081/2,结果如下所示:
1 | 1. { |
删除用户
以下代码,我们创建了 RESTful API deleteUser, 用于删除指定用户的详细信息,以下实例中,用户 id 为 2,server.js 文件代码如下所示:
1 | 1. var express = require('express'); |
接下来执行以下命令:
1 | 1. $ node server.js应用实例,访问地址为 http://0.0.0.0:8081 |
在浏览器中访问 http://127.0.0.1:8081/deleteUser,结果如下所示:
1 | 1. { user1: |
Node.js 多进程
Node.js 多进程
我们都知道 Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能。
每个子进程总是带有三个流对象:child.stdin, child.stdout 和 child.stderr。他们可能会共享父进程的 stdio 流,或者也可以是独立的被导流的流对象。
Node 提供了 child_process 模块来创建子进程,方法有:
- exec - child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。
- spawn - child_process.spawn 使用指定的命令行参数创建新进程。
- fork - child_process.fork 是 spawn() 的特殊形式,用于在子进程中运行的模块,如 fork(‘./son.js’) 相当于 spawn(‘node’, [’./son.js’]) 。与 spawn 方法不同的是,fork 会在父进程与子进程之间,建立一个通信管道,用于进程之间的通信。
exec() 方法
child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。
语法如下所示:
1 | 1. child_process.exec(command[, options], callback) |
参数
参数说明如下:
command**:** 字符串, 将要运行的命令,参数使用空格隔开
options :对象,可以是:
- cwd ,字符串,子进程的当前工作目录
- env,对象 环境变量键值对
- encoding ,字符串,字符编码(默认: ‘utf8’)
- shell ,字符串,将要执行命令的 Shell(默认:在 UNIX 中为/bin/sh, 在 Windows 中为cmd.exe, Shell 应当能识别 -c开关在 UNIX 中,或 /s /c 在 Windows 中。 在 Windows 中,命令行解析应当能兼容cmd.exe)
- timeout,数字,超时时间(默认: 0)
- maxBuffer,数字, 在 stdout 或 stderr 中允许存在的最大缓冲(二进制),如果超出那么子进程将会被杀死 (默认:200*1024)
- killSignal ,字符串,结束信号(默认:’SIGTERM’)
- uid,数字,设置用户进程的 ID
- gid,数字,设置进程组的 ID
callback :回调函数,包含三个参数 error, stdout 和 stderr。
exec() 方法返回最大的缓冲区,并等待进程结束,一次性返回缓冲区的内容。
实例
让我们创建两个 js 文件 support.js 和 master.js。
support.js 文件代码:
1 | 1. console.log("进程 " + process.argv[2] + " 执行。" ); |
master.js 文件代码:
1 | 1. const fs = require('fs'); |
执行以上代码,输出结果为:
1 | 1. $ node master.js |
spawn() 方法
child_process.spawn 使用指定的命令行参数创建新进程,语法格式如下:
1 | 1. child_process.spawn(command[, args][, options]) |
参数
参数说明如下:
command**:** 将要运行的命令
args**:** Array 字符串参数数组
options Object
- cwd String 子进程的当前工作目录
- env Object 环境变量键值对
- stdio Array|String 子进程的 stdio 配置
- detached Boolean 这个子进程将会变成进程组的领导
- uid Number 设置用户进程的 ID
- gid Number 设置进程组的 ID
spawn() 方法返回流 (stdout & stderr),在进程返回大量数据时使用。进程一旦开始执行时 spawn() 就开始接收响应。
实例
让我们创建两个 js 文件 support.js 和 master.js。
support.js 文件代码:
1 | 1. console.log("进程 " + process.argv[2] + " 执行。" ); |
master.js 文件代码:
1 | 1. const fs = require('fs'); |
fork 方法
child_process.fork 是 spawn() 方法的特殊形式,用于创建进程,语法格式如下:
1 | 1. child_process.fork(modulePath[, args][, options]) |
参数
参数说明如下:
modulePath: String,将要在子进程中运行的模块
args: Array 字符串参数数组
options:Object
- cwd String 子进程的当前工作目录
- env Object 环境变量键值对
- execPath String 创建子进程的可执行文件
- execArgv Array 子进程的可执行文件的字符串参数数组(默认: process.execArgv)
- silent Boolean 如果为true,子进程的stdin,stdout和stderr将会被关联至父进程,否则,它们将会从父进程中继承。(默认为:false)
- uid Number 设置用户进程的 ID
- gid Number 设置进程组的 ID
返回的对象除了拥有 ChildProcess 实例的所有方法,还有一个内建的通信信道。
实例
让我们创建两个 js 文件 support.js 和 master.js。
support.js 文件代码:
\1. console.log(“进程 “ + process.argv[2] + “ 执行。” );
master.js 文件代码:
1 | 1. const fs = require('fs'); |
Node.js JXcore 打包
Node.js JXcore 打包
Node.js 是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。
JXcore 是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。
介绍 JXcore 的打包功能。
JXcore 安装
下载 JXcore 安装包,并解压,在解压的的目录下提供了 jx 二进制文件命令,接下来我们主要使用这个命令。
步骤 1**、下载**
下载 JXcore 安装包 https://github.com/jxcore/jxcore-release,你需要根据你自己的系统环境来下载安装包。
1、Window 平台下载:Download(Windows x64 (V8))。
2、Linux/OSX 安装命令:
1 | 1. $ curl https://raw.githubusercontent.com/jxcore/jxcore/master/tools/jx_install.sh | bash |
如果权限不足,可以使用以下命令:
1 | 1. $ curl https://raw.githubusercontent.com/jxcore/jxcore/master/tools/jx_install.sh | sudo bash |
以上步骤如果操作正确,使用以下命令,会输出版本号信息:
1 | 1. $ jx --versionv0.10.32 |
包代码
例如,我们的 Node.js 项目包含以下几个文件,其中 index.js 是主文件:
1 | 1. drwxr-xr-x 2 root root 4096 Nov 13 12:42 images |
接下来我们使用 jx 命令打包以上项目,并指定 index.js 为 Node.js 项目的主文件:
1 | 1. $ jx package index.js index |
以上命令执行成功,会生成以下两个文件:
- index.jxp 这是一个中间件文件,包含了需要编译的完整项目信息。
- index.jx 这是一个完整包信息的二进制文件,可运行在客户端上。
载入 JX 文件
Node.js 的项目运行:
1 | 1. $ node index.js command_line_arguments |
使用 JXcore 编译后,我们可以使用以下命令来执行生成的 jx 二进制文件:
1 | 1. $ jx index.jx command_line_arguments |
更多 JXcore 安装参考:https://github.com/jxcore/jxcore/blob/master/doc/INSTALLATION.md。
更多 JXcore 功能特性你可以参考官网:https://github.com/jxcore/jxcore。
Node.js 连接 MySQL
Node.js 连接 MySQL
使用 Node.js 来连接 MySQL,并对数据库进行操作。
安装驱动
本教程使用了淘宝定制的 cnpm 命令进行安装:
\1. $ cnpm install mysql
连接数据库
在以下实例中根据你的实际配置修改数据库用户名、及密码及数据库名:
test.js 文件代码:
1 | 1. var mysql = require('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 的配置文件 |
更多说明可参见:https://github.com/mysqljs/mysql
数据库操作 ( CURD )
在进行数据库操作前,你需要将本站提供的 Websites 表 SQL 文件websites.sql 导入到你的 MySQL 数据库中。
测试的 MySQL 用户名为 root,密码为 123456,数据库为 test,你需要根据自己配置情况修改。
Node.js 连接 MongoDB
Node.js 连接 MongoDB
MongoDB 是一种文档导向数据库管理系统,由 C++ 撰写而成。
如何使用 Node.js 来连接 MongoDB,并对数据库进行操作。
安装 cnpm
本教程使用了淘宝定制的 cnpm 命令进行安装:
1 | 1. $ cnpm install mongodb |
接下来我们来实现增删改查功能。
创建数据库
要在 MongoDB 中创建一个数据库,首先我们需要创建一个 MongoClient 对象,然后配置好指定的 URL 和 端口号。
如果数据库不存在,MongoDB 将创建数据库并建立连接。
1 | 1. var MongoClient = require('mongodb').MongoClient; |
创建集合
我们可以使用 createCollection() 方法来创建集合:
1 | 1. var MongoClient = require('mongodb').MongoClient; |
数据库操作 ( CURD )
与 MySQL 不同的是 MongoDB 会自动创建数据库和集合,所以使用前我们不需要手动去创建。