文章目录
特点
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是单进程单线程应用程序,基于Google的V8引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。
Node.js 几乎每一个 API 都是支持回调函数的。
Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
第一个Node.js程序:Hello World!
console.log("Hello World");
node helloworld.js
NPM包管理工具
使用 npm 命令安装模块
npm 安装 Node.js 模块语法格式如下:
$ npm install <Module Name>
以下实例,我们使用 npm 命令安装常用的 Node.js web框架模块 express:
var express = require(‘express’);
全局安装与本地安装
npm 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有-g而已,比如
npm install express # 本地安装
npm install express -g # 全局安装
本地安装
- 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
- 可以通过 require() 来引入本地安装的包。
全局安装
- 将安装包放在 /usr/local 下或者你 node 的安装目录。
- 可以直接在命令行里使用
回调函数
Node.js 异步编程的直接体现就是回调。
例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。
创建 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
程序执行结束!
helloworld官网地址:www.runoob.com
事件循环
Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。
EventEmitter 类
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
可以参考Node.js EventEmitter教程
你可以通过require(“events”);来访问该模块。
创建 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.js的文件可以相互调用,Node.js提供了一个简单的模块系统。
模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。
Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
接下来我们就来创建 hello.js 文件,代码如下:
exports.world = function() {
console.log('Hello World');
}
在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require(’./hello’) 加载这个模块,然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。
代码 require(’./hello’) 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)
有时候我们只是想把一个对象封装到模块中,格式如下:
module.exports = function() {
// ...
}
例如:
//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;
这样就可以直接获得这个对象了:
//main.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。
不建议同时使用 exports 和 module.exports。
如果先使用 exports 对外暴露属性或方法,再使用 module.exports 暴露对象,会使得 exports 上暴露的属性或者方法失效。
原因在于,exports 仅仅是 module.exports 的一个引用。在 nodejs 中,是这么设计 require 函数的:
function require(...){
var module = {exports: {}};
((module, exports) => {
function myfn () {}
// 这个myfn就是我们自己的代码
exports.myfn = myfn; // 这里是在原本的对象上添加了一个myfn方法。
module.exports = myfn;// 这个直接把当初的对象进行覆盖。
})(module,module.exports)
return module.exports;
}
由于 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,非原生模块的文件模块。
函数
在JavaScript中,一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。
Node.js中函数的使用与Javascript类似,举例来说,你可以这样做:
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
}
execute(say, "Hello");
匿名函数
我们可以把一个函数作为变量传递。但是我们不一定要绕这个"先定义,再传递"的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:
function execute(someFunction, value) {
someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");
Node.js 路由
我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。
我们需要的所有数据都会包含在 request 对象中,该对象作为 onRequest() 回调函数的第一个参数传递。但是为了解析这些参数数据,我们需要额外的 Node.JS 模块,它们分别是 url 和 querystring 模块。
当然我们也可以用 querystring 模块来解析 POST 请求体中的参数,稍后会有演示。
Node.js 常用工具
util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心 JavaScript 的功能 过于精简的不足。
const util = require('util');
util.callbackify(original) 将 async 异步函数(或者一个返回值为 Promise 的函数)转换成遵循异常优先的回调风格的函数,例如将 (err, value) => … 回调作为最后一个参数。 在回调函数中,第一个参数为拒绝的原因(如果 Promise 解决,则为 null),第二个参数则是解决的值。
const util = require('util');
async function fn() {
return 'hello world';
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, ret) => {
if (err) throw err;
console.log(ret);
});
util.inspect(object,[showHidden],[depth],[colors]) 是一个将任意对象转换 为字符串的方法
util.isArray(object)
如果给定的参数 “object” 是一个数组返回 true,否则返回 false。
util.isRegExp(object)
如果给定的参数 “object” 是一个正则表达式返回true,否则返回false。
util.isDate(object)
如果给定的参数 “object” 是一个日期返回true,否则返回false。
Node.js GET/POST请求
获取GET请求内容
由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET请求的参数。
node.js 中 url 模块中的 parse 函数提供了这个功能。
获取 URL 的参数
我们可以使用 url.parse 方法来解析 URL 中的参数,代码如下:
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);
在浏览器中访问 http://localhost:3000/user?name=helloworld&url=www.runoob.com 然后查看返回结果:
获取 POST 请求内容
POST 请求的内容全部的都在请求体中,http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作。
比如上传文件,而很多时候我们可能并不需要理会请求体的内容,恶意的POST请求会大大消耗服务器的资源,所以 node.js 默认是不会解析请求体的,当你需要的时候,需要手动来做。
Express 框架
Express 简介
Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。
使用 Express 可以快速地搭建一个完整功能的网站。
Express 框架核心特性:
- 可以设置中间件来响应 HTTP 请求。
- 定义了路由表用于执行不同的 HTTP 请求动作。
- 可以通过向模板传递参数来动态渲染 HTML 页面。
路由
我们已经了解了 HTTP 请求的基本应用,而路由决定了由谁(指定脚本)去响应客户端请求。
在HTTP请求中,我们可以通过路由提取出请求的URL以及GET/POST参数。
接下来我们扩展 Hello World,添加一些功能来处理更多类型的 HTTP 请求。
var express = require('express');
var app = express();
// 主页输出 "Hello World"
app.get('/', function (req, res) {
console.log("主页 GET 请求");
res.send('Hello GET');
})
// POST 请求
app.post('/', function (req, res) {
console.log("主页 POST 请求");
res.send('Hello POST');
})
// /del_user 页面响应
app.get('/del_user', function (req, res) {
console.log("/del_user 响应 DELETE 请求");
res.send('删除页面');
})
// /list_user 页面 GET 请求
app.get('/list_user', function (req, res) {
console.log("/list_user GET 请求");
res.send('用户列表页面');
})
// 对页面 abcd, abxcd, ab123cd, 等响应 GET 请求
app.get('/ab*cd', function(req, res) {
console.log("/ab*cd GET 请求");
res.send('正则匹配');
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
})
执行以上代码:
$ node express_demo2.js
应用实例,访问地址为 http://0.0.0.0:8081
接下来你可以尝试访问 http://127.0.0.1:8081 不同的地址,查看效果。
比如 在浏览器中访问 http://127.0.0.1:8081/list_user
详细内容请到express
连接 MongoDB
MongoDB是一种文档导向数据库管理系统,由C++撰写而成。
本章节我们将为大家介绍如何使用 Node.js 来连接 MongoDB,并对数据库进行操作。
安装驱动
使用了淘宝定制的 cnpm 命令进行安装
$ cnpm install mongodb
接下来我们来实现增删改查功能。
创建数据库
要在 MongoDB 中创建一个数据库,首先我们需要创建一个 MongoClient 对象,然后配置好指定的 URL 和 端口号。
如果数据库不存在,MongoDB 将创建数据库并建立连接。
创建连接
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/runoob";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
console.log("数据库已创建!");
db.close();
});
创建集合(即表)
我们可以使用 createCollection() 方法来创建集合:
创建集合
var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/runoob';
MongoClient.connect(url, { useNewUrlParser: true }, function (err, db) {
if (err) throw err;
console.log('数据库已创建');
var dbase = db.db("runoob");
dbase.createCollection('site', function (err, res) {
if (err) throw err;
console.log("创建集合!");
db.close();
});
});
数据库操作( CURD )
与 MySQL 不同的是 MongoDB 会自动创建数据库和集合,所以使用前我们不需要手动去创建。
插入数据
以下实例我们连接数据库 runoob 的 site 表,并插入一条数据条数据,使用 insertOne():
插入一条数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var myobj = { name: "helloworld", url: "www.runoob" };
dbo.collection("site").insertOne(myobj, function(err, res) {
if (err) throw err;
console.log("文档插入成功");
db.close();
});
});
执行以下命令输出就结果为:
$ node test.js
文档插入成功
从输出结果来看,数据已插入成功。
我们也可以打开 MongoDB 的客户端查看数据,如:
show dbs
runoob 0.000GB # 自动创建了 runoob 数据库
show tables
site # 自动创建了 site 集合(数据表)
db.site.find()
{ “_id” : ObjectId(“5a794e36763eb821b24db854”), “name” : “helloworld”, “url” : “www.runoob” }
如果要插入多条数据可以使用 insertMany():
插入多条数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var myobj = [
{ name: '菜鸟工具', url: 'https://c.runoob.com', type: 'cn'},
{ name: 'Google', url: 'https://www.google.com', type: 'en'},
{ name: 'Facebook', url: 'https://www.google.com', type: 'en'}
];
dbo.collection("site").insertMany(myobj, function(err, res) {
if (err) throw err;
console.log("插入的文档数量为: " + res.insertedCount);
db.close();
});
});
res.insertedCount 为插入的条数。
查询数据
可以使用 find() 来查找数据, find() 可以返回匹配条件的所有数据。 如果未指定条件,find() 返回集合中的所有数据。
find()
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
dbo.collection("site"). find({}).toArray(function(err, result) { // 返回集合中所有数据
if (err) throw err;
console.log(result);
db.close();
});
});
以下实例检索 name 为 “helloworld” 的实例:
查询指定条件的数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var whereStr = {"name":'helloworld'}; // 查询条件
dbo.collection("site").find(whereStr).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
执行以下命令输出就结果为:
[ { _id: 5a794e36763eb821b24db854,
name: 'helloworld',
url: 'www.runoob' } ]
更新数据
我们也可以对数据库的数据进行修改,以下实例将 name 为 “helloworld” 的 url 改为 https://www.runoob.com:
更新一条数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var whereStr = {"name":'helloworld'}; // 查询条件
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();
});
});
执行成功后,进入 mongo 管理工具查看数据已修改:
db.site.find().pretty()
{
“_id” : ObjectId(“5a794e36763eb821b24db854”),
“name” : “helloworld”,
“url” : “https://www.runoob.com” // 已修改为 https
}
如果要更新所有符合条的文档数据可以使用 updateMany():
更新多条数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var whereStr = {"type":'en'}; // 查询条件
var updateStr = {$set: { "url" : "https://www.runoob.com" }};
dbo.collection("site").updateMany(whereStr, updateStr, function(err, res) {
if (err) throw err;
console.log(res.result.nModified + " 条文档被更新");
db.close();
});
});
result.nModified 为更新的条数。
删除数据
以下实例将 name 为 “helloworld” 的数据删除 :
删除一条数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var whereStr = {"name":'helloworld'}; // 查询条件
dbo.collection("site").deleteOne(whereStr, function(err, obj) {
if (err) throw err;
console.log("文档删除成功");
db.close();
});
});
执行成功后,进入 mongo 管理工具查看数据已删除:
db.site.find()
如果要删除多条语句可以使用 deleteMany() 方法
以下实例将 type 为 en 的所有数据删除 :
删除多条数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var whereStr = { type: "en" }; // 查询条件
dbo.collection("site").deleteMany(whereStr, function(err, obj) {
if (err) throw err;
console.log(obj.result.n + " 条文档被删除");
db.close();
});
});
obj.result.n 删除的条数。
排序
排序 使用 sort() 方法,该方法接受一个参数,规定是升序(1)还是降序(-1)。
例如:
{ type: 1 } // 按 type 字段升序
{ type: -1 } // 按 type 字段降序
按 type 升序排列:
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var mysort = { type: 1 };
dbo.collection("site").find().sort(mysort).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
查询分页
如果要设置指定的返回条数可以使用 limit() 方法,该方法只接受一个参数,指定了返回的条数。
limit():读取两条数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
dbo.collection("site").find().limit(2).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
如果要指定跳过的条数,可以使用 skip() 方法。
skip(): 跳过前面两条数据,读取两条数据
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
dbo.collection("site").find().skip(2).limit(2).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
删除集合
我们可以使用 drop() 方法来删除集合:
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
// 删除 test 集合
dbo.collection("test").drop(function(err, delOK) { // 执行成功 delOK 返回 true,否则返回 false
if (err) throw err;
if (delOK) console.log("集合已删除");
db.close();
});
});