赞
踩
前言:Node.js 是运行在服务器端的 JavaScript,基于 Chrome JavaScript 运行是建立的一个平台,Node.js是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8引擎执行Javascript的速度非常快,性能非常好;作为一个后端程序猿,如果想要部署一些高性能的服务, Node.js 还是要了解了解的
在官网上找到安装压缩包或者.msi 文件,一路 next 就行了,这两者区别就是压缩包解压之后需要配置环境变量,.msi 不需要,直接在注册表那里以及自动配置好环境变量,一条龙服务的
步骤
var http = require('http'); //载入http模块
// 使用 createServer 方法创建服务器,使用 listen 绑定
http.createServer(function (request, response) {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// 发送响应数据 "Hello World"
response.end('Hello World\n');
}).listen(8888);
// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');
把这个文件保存在 node文件下中的更目录下
然后 node server.js 运行
在浏览器就可以访问到了 hello world
分析 Node.js 的 HTTP 服务器:
第一行请求(require)Node.js 自带的 http 模块,并且把它赋值给 http 变量,接下来我们调用 http 模块提供的函数: createServer
这个函数会返回 一个对象,这个对象有一个叫做 listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号
随同 node.js 一起安装的包管理工具,能解决 NodeJS 代码部署上的问题
常见使用场景
使用 npm 命令安装 express
…
Node 自带交互式解释器,可以执行,读取用户输入,执行数据结构,打印输出结果,循环以上操作
启动 node 终端,在 cmd 命令键入 node
也可以支持循环
阻塞和非阻塞案例
阻塞:创建一个 input.txt 内容如下
Node.js 回调函数教程
创建一个 main.js 文件,代码如下
var fs = require("fs");
var data = fs.readFileSync('input.txt'); //代码会在这里阻塞
console.log(data.toString());
console.log("程序执行结束!");
执行结果如下
非阻塞案例
main2.js 文件代码
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
//如果读取错误,则抛出异常
if (err) {
return console.error(err);
}
//如果正确读取,则输出内容
console.log(data.toString());
});
console.log("程序执行结束!");
输出如下
可以看到,代码并没有在读取的时候阻塞,而是直接往下执行了,如果读取成功打印,读取失败则抛出异常
总结:阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内
Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供异步的执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高
Node.js 中的每一个 api 几乎都是支持回调函数,所以基本上所有的事件机制都是用观察者模型实现
Node.js 单线程类似进入一个while(True) 的事件循环,直到没有事件观察者退出,每个异步事件都生产一个事件观察者,如果有事件发生就调用该回调函数
//引入 event 模块 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.js 所有的异步 IO 操作在完成时都会发送一个事件到事件队列,Node.js 里面的许多对象都会分发事件:
所有这些产生事件的对象都是 events.EventEmitter 的实例
EventEmitter 对象如果在实例化时发生错误,会触发 error 事件,当添加新的监听器时,newListener 事件会被触发,当监听器被移除时,removeListener 会被触发
案例:1s 后会触发事件
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
// 绑定 some_event 事件
event.on('some_event',function () {
console.log('some_event 事件触发');
})
// 1000ms 后触发事件
setTimeout(function(){
event.emit('some_event');
}, 1000);
案例 2:EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('some_Event',function (arg1,arg2) {
console.log('listen1',arg1,arg2);
});
emitter.on('some_Event',function (arg1,arg2) {
console.log('listen2',arg1,arg2) ;
});
emitter.emit('some_Event','arg1参数','arg2参数');
案例 3:
var events = require('events'); var eventEmitter = new events.EventEmitter(); //监听器1 var listener1 = function listener1() { console.log('监听器 listen1 执行!'); } //监听器2 var listener2 = function listener2() { console.log('监听器 listen2 执行!'); } //绑定 connection 事件,处理函数为 listener1,listener2 eventEmitter.addListener('connection',listener1); eventEmitter.on('connection',listener2); var eventListeners = eventEmitter.listenerCount('connection'); console.log(eventListeners + '个监听器监听连接事件!'); // 触发 connection 事件 eventEmitter.emit('connection'); //移除监绑定的 listener1 函数 eventEmitter.removeListener('connection',listener1); console.log('listener1 不被监听!'); //触发连接事件 eventEmitter.emit('connection'); eventListeners = eventEmitter.listenerCount('connection'); console.log(eventListeners + '个监听器监听连接事件!'); console.log("程序执行完毕!");
$ node main.js
2 个监听器监听连接事件!
监听器 listener1 执行!
监听器 listener2 执行!
listener1 不再受监听!
监听器 listener2 执行!
1 个监听器监听连接事件!
程序执行完毕!
Error 事件
EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。
当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。
我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型,但是在处理 TCP 流或者二进制流,必须使用到二进制数据,因此在 Node.js 中定义一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓冲区
在 Node.js 中buffer 类是随内核一起发布的核心库,Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 node.js 中处理 IO 操作移动数据时,就有可能使用 buffer ,原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存
Buffer 与字符编码
Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换
const buf = Buffer.from('runnob','ascii');
console.log(buf.toString('hex'));
console.log(buf.toString('base64'));
//72756e6e6f62 cnVubm9i
创建 buffer 类
//创建一个长度为 10 ,且用 0 填充的 Buffer
const buf1 = Buffer.alloc(10);
//创建一个长度为10,且用 0x1 填充的 Buffer
const buf2 = Buffer.alloc(10,1);
//创建一个长度为10,但是未被初始化的 Buffer
//这个方法比调用 alloc 快,但是返回的 Buffer 实例可能包含
//旧数据,需要使用 fill 或者 writer 重写
const buf3 = Buffer.allocUnsafe(10);
//创建一个包含 [0x1,0x2,0x3]的buffer
const buf4 = Buffer.from([1,2,3]);
// 创建一个包含 utf-8 字节[0x74,0xc3,0xa9,0x73,0x74] 的buffer
const buf5 = Buffer.from('test');
//创建一个包含 Latin-1 字节[0x74,0xe9,0x73,0x74]的buffer
const buf6 = Buffer.from('test','latin1');
写入缓冲区
buf = Buffer.alloc(256);
len = buf.write('Node.js jiaocheng');
console.log("写入字节数:"+len); //17
从缓冲区中读取数据
实例
buf = Buffer.alloc(26)
for(var i = 0;i<26;++i){
buf[i] = i + 97;
}
console.log(buf.toString('ascii'));
console.log(buf.toString('ascii',0,5));
console.log(buf.toString('utf8',0,5));
console.log(buf.toString(undefined,0,5));
输出结果为
abcdefghijklmnopqrstuvwxyz
abcde
abcde
abcde
将 Buffer 对象转换为 Json 对象
const buf = Buffer.from([0x1,0x2,0x3,0x4,0x5]);
const json = JSON.stringify(buf)
console.log(json)
//输出 {"type":"Buffer","data":[1,2,3,4,5]}
const copy = JSON.parse(json,(key,value)=>{
return value && value.type == 'Buffer'?
Buffer.from(value.data):value;
});
console.log(copy);
//输出 <Buffer 01 02 03 04 05>
缓冲区合并
实例
var buffer1 = Buffer.from('Node.js 教程');
var buffer2 = Buffer.from('I like study');
// 返回一个多个成员合并的新 buffer 对象
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log('buffer3内容:'+buffer3.toString());
//输出 Node.js 教程I like study
缓冲区的比较
var buffer1 = Buffer.from('ABC')
var buffer2 = Buffer.from('ABCD');
var result = buffer1.compare(buffer2);
if (result < 0){
console.log(buffer1 + '在' + buffer2 + '之前');
}
else if(result > 0){
console.log(buffer1 + '在' + buffer2 + '之后');
}
else{
console.log(buffer1 + '在' + buffer2 + '相同');
}
拷贝缓冲区
var buf1 = Buffer.from('abcdefghijkl');
var buf2 = Buffer.from('Runnob');
//将 buf2 插入到 buf1 指定的位置上
buf2.copy(buf1,2);
console.log(buf1.toString()); // abRunnobijkl
裁剪缓冲区
//裁剪缓冲区
var buffer1 = Buffer.from('Runnob');
var buffer2 = buffer1.slice(0,2);
console.log(buffer2.toString());
Stream 是一个抽象的接,Node 很多对象实现了这个接口,例如对 http 服务器发起请求的 request 对象就是一个 stream ,还有 stdout 标准输出
Readable - 可读操作,Writable - 可写操作,Duplex - 可读可写操作,Transform - 操作被写入数据,然后读出结果
所有的 stream 对象都是 EventEmitter 的实例,常用的事件有
data- 当有事件可读时触发
end- 没有更多的数据可读时触发
error- 在接收和写入的过程中发生错误时触发
finish- 所有的数据已被写入到底层系统时触发
从流中读取数据
var fs = require('fs'); var data = ''; //创建可读流 var readerStream = fs.createReadStream('input.txt'); //设置编码 readerStream.setEncoding('utf8'); //处理流事件 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('执行完毕');
写入流
var fs = require('fs'); var data = "Node.js Stream 流教程"; //创建一个可写入的流 var writerStream = fs.createWriteStream('output.txt'); //使用 utf-8编码写入数据 writerStream.write(data,'utf8'); //标记文件末尾 writerStream.end(); writerStream.on('finish',function () { console.log('写入完成!'); }); writerStream.on('error',function (err) { console.log(err.stack); }); console.log('程序执行完毕!');
管道流:管道提供了一个输出流到到输入流的机制,通常我们用于从一个流中获取数据传递到另外一个流中
实例:读取一个文件的内容,写入到另外一个文件中
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('程序执行完毕!');
链式流 :链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作
创建 compress.js 文件
var fs = require("fs");
var zlib = require('zlib');
// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));
console.log("文件压缩完成");
创建 decompress.js 文件
var fs = require('fs');
var zlib = require('zlib');
//解压
fs.createReadStream('input.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('input2.txt'));
console.log('文件解压完成!');
为了让Node.js 的文件可以互相调用,Node.js 提供了一个简单的模块系统
模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的,所以一个 Node.js 文件就是一个模块,这个文件可能是 JavaScript 代码,JSON 后者 可编译的 C/C++ 编译
引入模块
在 Node.js 中,引入一个模块非常简单,例如创建一个 main.js 文件引入 hello 模块
var hello = require('./hello');
hello.world();
创建 hello 模块
exports.world = function () {
console.log('Hello World');
}
把对象封装到模块中
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('MaZe');
hello.sayHello();
require 加载模块的过程
优先级:文件模块-》原生模块-》查找文件模块,根据扩展名载入
函数名可以作为另一个函数的参数进行传递
function say(hello){
console.log(hello);
}
function excute(someFunction,value) {
someFunction(value);
}
excute(say,"Hello MaZe");
匿名函数
function excute(someFunction,value) {
someFunction(value);
}
excute(function (word) {
console.log(word)
},"hello");
函数传递是如何让 http 服务器工作的
var http = require("http"); // 写法一: http.createServer(function (request,response) { response.writeHead(200,{ "Content-Type":"text/plain" }); response.write("Hello World"); response.end(); }).listen(8887); //写法二: function onRequest(request,response) { response.writeHead(200,{"Content-Type":"text/plain"}); response.write('Hello World'); response.end(); }; http.createServer(onRequest).listen(8888);
我们要为路由提供请求的 url 和其它需要的 get 以及 post 参数,随后路由器根据这些数据来执行相应的代码
因此我们需要查看 http 请求,从中提取出 url 以及 get/post 参数,这一功能我们暂定作为服务器的功能
首先我们定义 onRequest ,用来找出浏览器的 url 路径
server.js 文件
var http = require("http"); var url = require("url"); const { route } = require("./root"); function start() { 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;
编写路由 root.js 文件
function route(pathname) {
console.log("About to route a request for"+pathname);
}
exports.route = route;
这个时候把路由函数注册到服务器上
var server = require("./server");
var router = require("./root");
server.start(router.route);
随后请求一个URL,你将会看到应用输出相应的信息,这表明我们的HTTP服务器已经在使用路由模块了,并会将请求的路径传递给路由
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。