# 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,来访问静态目录下的资源
Last Updated: 7/15/2020, 10:34:45 PM