# Nodejs
# 一、安装创建第一个服务
node.js是运行在服务端的JavaScript,node.js是一个事件驱动I/O服务端JavaScript环境
# 1】使用node命令来执行js文件
node xxx.js #文件名
# 2】使用nodejs来创建一个http服务,要由三个部分组成,
①引入模块,require;
②创建服务器;
③处理响应与请求;
如下demo2.js代码
//创建一个服务
//引入一个模块,把这个模块保存在_http变量中
var _http = require('http');
//创建服务器
_http.createServer((request,response)=>{
//http头部信息
//状态码:200
//内容的类型:text/plain,text/html,text/xml(用的很少)
response.writeHead(200,{'Content-Type':'text/plain'});
//向客户端发送数据
response.end('第一个http服务')
}).listen(5249)
console.log('5249,已经创建。')//服务器端打印结果
此时在浏览器输入http://localhost:5249可以看到打印的“第一个http服务”
# text/plain:纯文本
# text/html:以html形式输出
# 3】在nodejs中,操作文件使用的是fs模块
var _fs=require('fs')
//同步读取文件
var _data = _fs.readFileSync("text.txt")//参数就是文件名
console.log(_data)//打印出:读取文件的二进制流
console.log(_data.toString())//内容读出来
# 二、同步和异步
nodejs它的异步编程的直接体现就是,“异步回调”。
通过异步处理高并发
# 1】阻塞的例子(同步)
这是一个阻塞的文件读取的例子
var _fs=require('fs')
//同步读取文件
var _data = _fs.readFileSync("text.txt")//参数就是文件名
console.log(_data)//打印出:读取文件的二进制流
console.log(_data.toString())//内容读出来
//这种情况就已经形成了阻塞
console.log('----')
//以上都是按顺序依次执行代码
# 2】非阻塞的例子(异步)
这是一个基于回调的非阻塞的文件读取的例子
var _fs=require('fs')
//异步读取文件
//readFile()方法是需要传递两个参数,第一个是文件名,第二个是一个回调函数(又是传入两个参数,第一个是错误信息,第二个是读取的二进制流数据)
_fs.readFile("text.txt",(err,_data)=>{
if(err) return err
console.log(_data.toString())
})
console.log('----')
//先执行同步方法打印出'----',然后再执行异步方法打印出文件数据,形成非阻塞
# 三、事件
客户端的事件是监听用户操作触发事件;nodejs中的事件是监听代码执行到了什么位置触发事件
NodeJs它是一个单进程单线程的引用程序,但是因为V8引擎提供了异步执行回调的接口,通过这些接口处理大的并发请求,性能很高。
nodejs它基本上,所有的事件机制,都是基于设计模式中的观察者模式来实现的。
简单的说,nodejs使用事件驱动的模型,每当接受到一个请求,就把它关掉进行处理,当这个请求完成,就把它放到处理队列当中,最后把结果返回给用户。
因为它的服务一直是处理接受请求,但不等待任何的读写操作,所以性能很高,这也叫“非阻塞式的IO或是事件驱动IO”
# 1】events模块
NodeJs的事件是使用events模块,
var xx = require('events')
通过实例化它里面的EventEmitter类,通过这个event实例来绑定和监听事件。
var event = new xx.EventEmitter()
绑定事件:event.on(...)
监听事件的触发:event.emit(...)
# 例子
var _events=require('events')
console.log(_events)//使用node+文件名,使用服务,打印出里面的类
//EventEmitter类的一个实例
var _EventEmitter = new _events.EventEmitter()
console.log(_EventEmitter)
//这里的事件名完全不同于浏览器上的各种事件,完全只是一个“标识”
//它其实是'xx123'名,绑定了一个回调函数
_EventEmitter.on('xx123',function(){
console.log('xx123 事件被触发了')
})
//触发名字为'xx123'的一个事件
_EventEmitter.emit('xx123')
console.log(_EventEmitter)
//执行在命令行中使用'node 文件名.js'开启服务之后,会依次打印:
/*
EventEmitter{domain:null,_events:{},_eventsCount:0,_maxListeners:undefined}
xx123 事件已经被触发
EventEmitter{domain:null,_events:{xx123:[Function]},_eventsCount:1,_maxListeners:undefined}
*/
# 2】异步io操作
nodejs里面所有异步的io操作,都会在完成时,发送一个事件到事件队列中。events这个模块只有一个对象events.EventEmitter,它的核心:“事件触发与监听功能的封装”。
# 例子
var _eventEmitter=require('events').EventEmitter()
var _event=new _eventEmitter()
_event.on('timeout',function(){
console.log('timeout 事件已经被触发')
})
setTimeout(function(){
_event.emit('timeout')
},2000)
console.log(_event)//以后被绑定
//两秒后打印出'timeout 事件已经被触发'
# 3】EventEmitter类
EventEmitter,它的每个事件都是由:1、事件名,是一个字符串;2、若干个参数;
# 例子:传参
可以在一个实例上,通过on方法绑定多个事件,然后通过同一个emit方法触发,并传参
var _eventEmitter=require('events').EventEmitter()
var _event=new _eventEmitter()
_event.on('xx1',function(arg1,arg2){
console.log('xx111111 事件已经被触发',arg1)
})
_event.on('xx1',function(arg1,arg2){
console.log('xx222222 事件已经被触发',arg1)
})
_event.emit('xx1','这是1','这是2')
//打印出'xx111111 事件已经被触发 这是1'
//打印出'xx222222 事件已经被触发 这是2'
# 4】观察者模式
设计模式中的观察者模式:
需要有发布者:publicFn;
还有订阅者:subFn;
主体对象:depFn;
发布者,通过主题对象,发出的消息:notifyFn;
//a,b,c是订阅
function a(){}
function b(){}
function c(){}
//xxx是发布
function xxx(){
a()
b()
c()
}
xxx()//这是发布者,发出的消息
又例如:
//发布者
var _pub={
publicFn:function(){
_depFn.notifyFn()
}
}
//订阅者
var _sub1={
xxsub:function(){
console.log('xx sub1')
}
}
var _sub2={
xxsub:function(){
console.log('xx sub2')
}
}
var _sub3={
xxsub:function(){
console.log('xx sub3')
}
}
//一个主题对象
function depFn(){
this.subs=[_sub1,_sub2,_sub3]
}
depFn.prototype.notifyFn=function(){
this.subs.forEach(function(item){
item.xxsub()
})
}
var _depFn=new depFn()
//发布者_pub,通过主题对象,发出的消息
/*
发布者只执行.publicFn()发出消息的方法,至于这个消息,具体怎么去执行,它不管
*/
_pub.publicFn()
# 四、buffer缓存
nodejs的开发语言就是js,JavaScript语言本身只有字符串数据类型,没有二进制数据类型。nodejs有时会操作一些文件,或是tcp流之类的东西,那么久必须要操作二进制数据。
因此,在nodejs中,有一个Buffer类,它用来创建一个专门存放二进制数据的缓存区。Buffer类是随nodejs的核心一起安装的,这些原始数据是存储在Buffer类的实例里,一个Buffer类相当于是一个整数的数组,它相当于是划出了一块自己的内存空间。Buffer类的实例,它用于表示编码字符的序列,它支持utf-8,base64,acsii等。
# 1】创建一个Buffer类
1、Buffer.alloc,它是返回一个指定大小的Buffer实例。
const buf1=Buffer.alloc(10)
//创建了一个长度为10,并且用0填充的Buffer
2、将Buffer转换成JSON对象
Buffer.form(array),它返回的是一个被array的值初始化后的新Buffer实例(注意:array之中的元素只能是数字,否则会被0覆盖)
const buf=Buffer.form([0x1,0x2,0x3,0x4,0x5])//以0x开头就是十六进制数
console.log(buf)//<Buffer 01 02 03 04 05>
const _json=JSON.stringify(buf)
console.log(_json)//{"type":"Buffer","data":[1,2,3,4,5]}
console.log(typeof _json)//string
const json=JSON.parse(_json)
console.log(json)//{type:'Buffer',data:[1,2,3,4,5]}
console.log(typeof json)//object
JSON.stringify(),用于将一个JavaScript值(对象或者数组),转换成一个JSON字符串
JSON.parse(),用于将json字符串转换成json对象
# 2】使用Buffer实例
用Buffer实例的write()方法写入内容,它返回的是写入的字节数。
用Buffer实例的toString()从缓冲区读取数据(注意可以向方法内传参)
const buf1=Buffer.alloc(10)
let num=buf1.write('hhhh')
console.log(num)//4
console.log(buf1.toString())//hhhh
console.log(buf1.toString('utf-8',0,2))//hh
用Buffer转换成JSON对象
buf.toJSON()//它返回值是一个json对象
# 五、Stream流
流,就是在线读取文件
1】在nodejs当中,Stream有四种流类型:
- Readable:可读操作
- Writable:可写操作
- Duple:可读可写操作
- Transform:操作被写入数据,然后读出结果
2】所有的Stream对象,都是EventEmitter的实例
常用的事件:
- data:当有数据可读取时触发;
- end:没有更多的数据可读取时触发;
- error:在接受和写入过程中,发生错误时触发;
- finish:所有的数据已经被写入之后,触发;
3】虽然是从流当中去读取文件,但我们其实依然是在操作文件,所以还是要用到nodejs当中的fs模块
//引入模块
var fs = require('fs')
//声明data变量,后面将数据读取到的变量中
var data = ''
//创建一个可读的流,createReadStream
var readStream=fs.createReadStream('inputDemo.txt')
//设置可读流的文件编码,utf-8
readStream.setEncoding('utf8')
//处理流的事件,data,end,error等
readStream.on('data',function(_d){
data +=_d
})
readStream.on('end',function(){
console.log(data)//输出
})
console.log('读取完毕')
4】写入流
//引入模块
var fs = require('fs')
//要写入的内容
var data='哈哈哈哈哈哈'
//要创建一个可以写入的流
var writeStream=fs.createWriteStream('xxwrite.txt')
//设置编码
writeStream.write(data,'utf8')
//标记文件末尾
writeStream.end()
writeStream.on('finish',function(){
console.log('写入完成啦')
})
console.log('执行完毕')
5】管道流(在线看视频就是管道流)
nodejs中的管道流,提供了一个从输出流到输入流的机制。
就是从一个流当中获取数据,并传递到另一个流当中。
主要用到.pipe()
方法
//引入模块
var fs = require('fs')
//创建一个可读的流,createReadStream
var readStream=fs.createReadStream('inputDemo.txt')
//要创建一个可以写入的流
var writeStream=fs.createWriteStream('bbwrite.txt')
//管道流操作
readStream.pipe(writeStream)
console.log('执行完毕')
6】链式流(压缩和解压缩的例子)
就是从一个输出流当中,读取数据,创建了多个流来操作这个输出流的数据的机制。
链式流一般来操作管道流
//引入模块
var fs = require('fs')
var zlib=require('zlib')//其中Gzip压缩文件,Gunzip是解压缩文件
//压缩文件
//创建一个可读的流,createReadStream
var readStream=fs.createReadStream('inputDemo.txt')
//要创建一个可以写入的流
var writeStream=fs.createWriteStream('bbwrite.txt.gz')
//操作管道流,形成链式流,压缩文件
readStream.pipe(zlib.createGzip()).pipe(writeStream)
console.log('压缩完啦')
//解压文件
//创建一个可读的流,createReadStream
var readStream=fs.createReadStream('bbwrite.txt.gz')
//要创建一个可以写入的流
var writeStream=fs.createWriteStream('ccwrite.txt')
//操作管道流,形成链式流,解压缩文件
readStream.pipe(zlib.createGunzip()).pipe(writeStream)
console.log('解压成功')
# 六、模块系统
文件和模块是一一对应的,一个nodejs
的文件,就是一个模块。
# 1】nodejs提供了两个对象:
exports
:它是模块公开的接口require
:用来获取外部的模块的接口
# 2】创建模块的二种方式
exports.方法名=function(){}
:只公开一个方法module.exports=function(){}
:把整个模块都全部的公开出来
# 七、get请求
获得get请求的内容,它的内容是在url的?号之后的部分
util
(是nodejs的工具对象,它有许多许多的方法)util.inspect
(用来把对象转成字符串)url
(是nodejs的url模块)url.parse
(解析url请求返回一个对象)
//get请求
var http=require('http')//引入需要的依赖
var util=require('util')
var url=require('url')
//创建一个http服务
http.createServer(function(req,resp){
resp.writeHead(200,{
'Content-Type':'text/plain;charset=utf-8'
})
console.log(typeof url.parse(req.url))//object,不能直接用end方法发送到页面
resp.end(util.inspect(url.parse(req.url)))//.end方法只能向页面发送字符串
}).listen(5642)
console.log('5642,已经启动')
通过页面url的get请求,返回对应的信息到页面
//get请求
var http=require('http')//引入需要的依赖
var url=require('url')
http.createServer(function(req,resp){
resp.writeHead(200,{
'Content-Type':'text/plain;charset=utf-8'
})
console.log(url.parse(req.url))
var _param=url.parse(req.url,true).query//使用url.parse(req.url)对象里面的query属性获取url里面的参数
resp.write('姓名:'+ _param.name)
resp.write('\n')
resp.write('年龄:'+ _param.age)
resp.end()
}).listen(5642)
console.log('5642,已经启动')
# 八、post请求
- querystring:用来解析url
- querystring.parse():用于将一个字符串,转为对象
//post请求
var http=require('http')//引入需要的依赖
var querystring=require('querystring')
//表单
var _formHtml=
'<form method="post">'+
'网站名:<input name="name"><br>'+
'年龄:<input name="age"><br>'+
'<input type="submit">'+
'</form>'
http.createServer(function(req,resp){
var body=''
req.on('data',function(_d){
body += _d
})
req.on('end',function(){
body =querystring.parse(body)
console.log(body)//已经转成对象了
resp.writeHead(200,{
'Content-Type':'text/html;charset=utf-8'
})
if(body.name != null){
res.write('姓名:'+body.name)
res.write('<br>')
res.write('年龄:'+body.age)
}else{
res.write(_formHtml)
}
res.end()
})
}).listen(5642)
console.log('5642,已经启动')
# 九、express框架
express是一个简洁的nodejs web应用框架,它提供一些功能,可以帮助你快速的搭建一个完整的网站。
# 1】核心特性:
- 可以使用中间件来响应http的请求;
- 可以设置一些简单的路由(是手动的);
# 2】安装Express
npm install express --save
--save的意思是,安装在你的开发目录。而不是全局安装。
# 3】使用express创建一个服务
//使用express创建一个服务
var express=require('express')
var app=express()
//这里的'/'就是路由的根路径
app.get('/',function(req,res){
res.send('第一个express的例子,创建一个服务')
})
app.listen(5482,function(){
console.log('5482 已经启动')
})
//只有用浏览器访问localhost:5482
# 4】express路由
简单的路由
//使用express创建一个服务
var express=require('express')
var app=express()
//这里的'/'就是路由的根路径
app.get('/',function(req,res){
res.send('这是根页面')
})
app.get('/a',function(req,res){
res.send('这是a页面')
})
app.get('/a/a1',function(req,res){
res.send('这是a1页面')
})
app.get('/b',function(req,res){
res.send('这是b页面')
})
app.get('/b/b1',function(req,res){
res.send('这是b1页面')
})
app.listen(5482,function(){
console.log('5482 已经启动')
})
//只有用浏览器访问localhost:5482
# 5】静态目录
Express有内置的中间件的设置,可以用来设置静态目录,express.static()
可以让我们通过http的方式,来访问静态文件。
它的效果和我们把文件放到phpstudy服务器上面的效果是一样的。
//使用express创建一个服务
var express=require('express')
var app=express()
//设置静态目录
app.use(express.static('xxxx'))//静态目录必须与node_modules处于相同的路径
app.listen(5482,function(){
console.log('5482 已经启动')
})
//只有用浏览器访问localhost:5482,来访问静态目录下的资源