1️⃣ Node.js
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。
Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快,性能非常好。
1️⃣ Node.js 有什么特点
文件的读写,数据的读写( I:input:写 / O:output:读 )I/O操作
2️⃣ 优点
异步非阻塞的 I/O( I/O线程池 )
特别适用于 I/O 密集型应用 ( 频繁操作 I/O 就属于 I/O 密集型 )
事件循环机制( 独有的一套,与浏览器不一样 )
单线程(成也单线程,败也单线程)
跨平台( 几乎常见的语言都支持 )
简单web交互模型:
2️⃣ 缺点
回调函数嵌套太多、太深( 俗称回调地狱 )
单线程,处理不好 CPU 密集型任务 ( 请求密集型 )
1️⃣ Node 与 Java 服务器的区别
java服务端一个线程处理一个请求,一对一的模式,客户端发出请求,服务端发送到数据库,这个单线程就单独处理这个请求,数据没有回来之前,再次发送请求时再次在服务端打开一个单线程处理请求。
node服务端一个线程处理多个请求,一对多的模式,客户端发出请求,服务端发送到数据库,在数据回来之前服务端可以处理第二个请求,如果第一个数据获取成功,这返回处理第一个数据。
1️⃣ Node 中函数的特点
Node 中任何一个模块( js 文件 )都被一个看不见的外层函数所包裹
function (exports, require, module, __filename, __dirname) {
console.log(arguments.callee.toString()); // 外层就是包裹的外层函数( 匿名函数 )
}
// 为什么要设计这个外层函数(这个外层函数有什么作用?)
// 1).用于支持模块化语法
// 2).隐藏服务器内部实现(从作用域角度去看)
exports:用于支持 CommonJs 模块化规范的暴露语法
require:用于支持 CommonJs 模块化规范的引入语法
module:用于支持 CommonJs 模块化规范的暴露语法
__filename:当前运行文件的绝对路径
__dirname:当前运行文件所在文件夹的绝对路径
1️⃣ Node 中的 global
node 禁止了函数中的 this 指向 global,输出 global 直接使用 global
2️⃣ 浏览器端 js 的组成部分
BOM - window 浏览器对象模型
DOM - document 文档对象模型
ES规范
2️⃣ Node js 的组成部分
几乎包含了所有 ES 规范
没有了 window 取而代之的是 global
2️⃣ global 的一些常用属性
clearImmediate:清空立即执行函数
clearInterval:清除循环定时器
clearTimeout:清除延迟定时器
setImmediate:设置立即执行函数
setInterval:设置循环定时器
setTimeout:设置延迟定时器
1️⃣ Node 中的事件循环模型
第一个阶段:timers ( 定时器阶段—setTimeout,setInterval )
1. 开始计时
2. 执行定时器的回调
第二个阶段:pending callbacks (系统阶段)
第三个阶段:idle, prepare (准备阶段)
第四个阶段:poll( 轮询阶段,核心 )
1. 如果回调队列里有待执行的回调函数
1. 从回调队列中取出回调函数,同步执行(一个一个执行),直到回调队列为空了,或者达到系统最大限度。
2. 如果回调队列为空
1. 如果有设置过setImmediate
1. 进入下一个check阶段,目的:为了执行setImmediate所设置的回调。
2. 如果未设置过setImmediate
1. 在此阶段停留,等待回调函数被插入回调队列。
2. 若定时器到点了,进入下一个check阶段,原因:为了走第五阶段,随后走第六阶段,随后第一阶段( 最终目的 )
第五个阶段:check ( 专门用于执行 setImmediate 所设置的回调 )
第六个阶段:close callbacks ( 关闭回调阶段 )
process.nextTick() —— 用于设置立即执行函数( “VIP”——-能在任意阶段优先执行 )
//延迟定时器
setTimeout(()=>{
console.log('setTimeout所指定的回调函数执行了')
})
//立即执行函数(回调)
setImmediate(()=>{
console.log('我是setImmediate指定的回调')
})
//立即执行函数(回调)
process.nextTick(()=>{
console.log('process.nextTick所指定的回调执行了')
})
console.log('我是主线程上的代码')
// Node 执行顺序
我是主线程上的代码
process.nextTick所指定的回调执行了
setTimeout所指定的回调函数执行了
我是setImmediate指定的回调
注意:setTimeout 与 setImmediate 的先后与主线程是否有代码和 setTimeout 设置时间的长短有关系
1️⃣ Node 中的文件读取与写入
2️⃣ Node中的文件系统
在NodeJs中有一个文件系统,所谓的文件系统,就是对计算机中的文件进行增删改查等操作。
在NodeJs中,给我们提供了一个模块,叫做fs模块(文件系统),专门用于操作文件。
fs模块是Node的核心模块,使用的时候,无需下载,直接引入。
2️⃣ 简单文件写入 - 异步文件写入
简单文件写入和简单文件读取,都是一次性把所有要读取或要写入的内容加到内存中红,容易造成内存泄露。
fs.writeFile(file, data,[options], callback)
1. file:要写入的文件路径+文件名+后缀
2. data:要写入的数据
3. options:配置对象 ( 可选参数 )
1. encoding:设置文件的编码方式,默认值:utf8 ( 万国码 )
2. mode:设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
1. 0o111:文件可被执行的权限 .exe .msc 几乎不用,linux 有自己一套操作方法。
2. 0o222:文件可被写入的权限
3. 0o444:文件可被读取的权限
3. flag:打开文件要执行的操作,默认值是 'w'
1. a :追加
2. w :写入
4. callback:回调函数
1. err:错误对象
5. 在Node中有这样一个原则:错误优先
// 写入文件的基本操作
// 引入内置的fs模块
let fs = require('fs')
// 调用 writeFile 方法
// Node.js 中,__dirname 总是指向被执行 js 文件的绝对路径
fs.writeFile(__dirname + '/demo.txt', '写入的文字', {
mode: 0o666, // 可不写 - 默认值
flag: 'w' // 可不写 - 默认值
}, (err) => {
if (err) {
console.log(`写入失败:${err}`);
} else {
console.log(`写入成功`);
}
})
fs.writeFile(__dirname + '/demo.txt', '写入的文字 - 追加', {
mode: 0o666,
flag: 'a'
}, (error) => {
if (error) {
console.log(`写入失败:${error}`);
} else {
console.log(`写入成功`);
}
})
2️⃣ 简单文件读取
fs.readFile(path[, options], callback)
1. path:要读取文件的路径+文件名+后缀
2. options:配置对象(可选)
3. callback:回调
1. err:错误对象
2. data:读取出来的数据
let fs = require('fs')
fs.readFile(__dirname + '/demo.txt', function (err, data) {
if (err) {
console.log(err)
} else {
console.log(data) // 读取出来的数据为 Buffer 格式
}
// 将读取的文件写入另一个文件
fs.writeFile('../demo1.txt', data, {
flag: 'a'
}, (err) => {
if (err) {
console.log(`写入失败:${err}`);
} else {
console.log(`写入成功`);
}
})
})
2️⃣ 流式文件写入
fs.createWriteStream(path,[options])
1. path:要写入文件的路径+文件名+文件后缀
2. options:配置对象(可选参数)
1. flags:打开文件要执行的操作,默认值是 'w'
2. encoding :设置文件的编码方式,默认值:utf8 ( 万国码 )
3. fd : 文件统一标识符,linux下文件标识符
4. mode :设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
1. 0o111:文件可被执行的权限 .exe .msc 几乎不用,linux 有自己一套操作方法。
2. 0o222:文件可被写入的权限
3. 0o444:文件可被读取的权限
5. autoClose:自动关闭 - 关闭流时执行自动关闭文件,默认值:true
6. emitClose:强制关闭 - 检测到某些问题时强制关闭文件,默认值:false
7. start:写入文件的起始位置( 写入文件前边的空格缩进 - 偏移量 )
// 引入内置的fs模块
let fs = require('fs')
//创建一个可写流
let ws = fs.createWriteStream(__dirname + '/demo.txt', {
start: 10
})
// 监测流的状态 - 只要用到了流,就必须检测流的状态
ws.on('open', function () {
console.log('可写流打开了')
})
ws.on('close', function () {
console.log('可写流关闭了')
})
//使用可写流写入数据
ws.write('第一次 \n')
ws.write('第二次 \n')
ws.write('第三次 \n')
// ws.close() 如果在 Node 的 8 版本中,使用此方法关闭流会造成数据丢失
ws.end() // 在 Node 的 8 版本中,要用 end 方法关闭流
2️⃣ 流式文件读取
fs.createReadStream(path[, options])
1. path:要读取的文件路径+文件名+后缀
2. options:
1. flags:打开文件要执行的操作,默认值是 'w'
2. encoding :设置文件的编码方式,默认值:utf8 ( 万国码 )
3. fd : 文件统一标识符,linux下文件标识符
4. mode :设置文件的操作权限,默认值是:0o666 = 0o222 + 0o444
5. autoClose:自动关闭 - 关闭流时执行自动关闭文件,默认值:true
6. emitClose:强制关闭 - 检测到某些问题时强制关闭文件,默认值:false
7. start:起始偏移量
8. end:结束偏移量
9. highWaterMark:每次读取数据的大小,默认值是 64 * 1024
let {
createReadStream,
createWriteStream
} = require('fs');
//创建一个可读流
let rs = createReadStream(__dirname + '/demo.mp3', {
highWaterMark: 10 * 1024 * 1024,
//start:60000,
//end:120000
})
// 创建一个可写流
let ws = createWriteStream('../demo1.mp3')
// 只要用到了流,就必须监测流的状态
rs.on('open', function () {
console.log('可读流打开了')
})
rs.on('close', function () {
console.log('可读流关闭了')
ws.close() // 在数据读取完毕之后关闭可写流
})
ws.on('open', function () {
console.log('可写流打开了')
})
ws.on('close', function () {
console.log('可写流关闭了')
})
// 给可读流绑定一个 data 事件,就会触发可读流自动读取内容。
rs.on('data', function (data) {
// Buffer 实例的 length 属性,是表示该 Buffer 实例占用内存空间的大小
console.log(data.length) // 输出的是 65536,每次读取 64KB 的内容
ws.write(data)
// ws.close() // 若在此处关闭流,会写入一次,后续数据丢失
})
// ws.close() // 若在此处关闭流,导致无法写入数据
1️⃣ Node 原生服务器
// 原生 node 搭建最简单的服务器
// 引入 node 内置的 http 模块
let http = require('http');
// 创建服务对象
let server = http.createServer(function (request, response) {
response.end('ok')
})
// 指定服务器的运行端口号( 绑定端口监听 )
server.listen(3000, function (err) {
if (!err) console.log('服务器启动成功了')
else console.log(err)
})
// 原生 node 搭建最简单的服务器( 详细版 )
// 引入 node 内置的 http 模块
let http = require('http');
//引入一个内置模块,用于解析 key=value&key=value.....这种形式的字符串为 js 中的对象
//备注:
// 1.key=value&key=value.....的编码形式:urlencoded 编码形式。
// 2.请求地址里携带urlencoded编码形式的参数,叫做:查询字符串参数(query参数)。
//引入的 qs 是一个对象,该对象身上有着很多有用的方法,最具代表性的:parse(),会解析 urlencoded 编码
let qs = require('querystring')
// 创建服务对象
let server = http.createServer(function (request, response) {
// (1).request:请求对象,里面包含着客户端给服务器的 “ 东西 ”
// (2).response:响应对象,里面包含着服务器要返回给客户端的 “ 东西 ”
// 获取客户端请求方式
if (request.method.toUpperCase() === 'POST') {
console.log('使用POST请求');
} else if (request.method.toUpperCase() === 'GET') {
console.log('使用GET请求')
//获取客户端 get 请求携带过来的 urlencoded 编码形式的参数
let params = request.url.split('?')[1];
// 将参数转换为对象
let objParams = qs.parse(params);
// 如果知道参数都有什么 使用结构赋值取出每一个参数
let {
name,
age
} = objParams;
// 设置响应头的编码格式 让浏览器认识相应的数据
response.setHeader('content-type', 'text/html;charset=utf-8');
// 相应数据
response.end(`<h1>姓名:${name} --- 年龄:${age}`);
}s
})
// 指定服务器的运行端口号( 绑定端口监听 )
server.listen(3000, function (err) {
if (!err) {
console.log('服务器启动成功了')
} else {
console.log(err)
}
})