node笔记

node做web后端优势

CMD 终端基础命令

- 开始菜单 --> 运行 --> CMD --> 回车
- Win + R --> CMD --> 回车
- 常用的指令
	dir 			列出当前目录下的所有文件
	cd 目录名   		进入到指定的目录
	md 目录名		创建一个文件夹
	rd 目录名		删除一个文件夹
-目录
	. 表示当前目录
	..  表示上一级目录
-环境变量(window系统中的变量 )
	- 当我们在命令行窗口打开一个文件,或调用一个程序时,
		系统会首先在当前目录下寻找文件程序,如果找到了则直接打开
		如果没有找到则会依次到环境变量path的路径中寻找,直到找到为止
		如果没找到则报错
		类似于作用域链
	-所以我们可以将一些经常需要访问的程序和文件的路径添加到path中,
		这样我们就可以在任意位置来访问这些文件和程序了


npm基础命令

	- npm -v 查看版本
	- npm version  查看所有模块的版本
	- npm 帮助说明
	- npm search 包名  搜索模块包
	- npm remove / r 删除包
⭐ 	- npm install 包名 --save  安装包并添加到依赖中
	- npm install / i 下载当前项目所依赖的包
	- npm install 包名 -g 全局安装包(
	- 全局安装的包一般都是一些工具,不是在项目里用,而是在计算机里用)
	- npm install 包名 -registry = 地址 从镜像源安装
	- npm config set registry 地址  设置镜像源

	从淘宝镜像下载包
	npm install -g cnpm --registry=https://registry.npm.taobao.org

	npm init -y 创建一个node项目 package.json说明创建成功

Buffer(缓冲区)【作用:存储二进制数据】

从结构上看Buffer非常像一个数组,它的元素为16进制的两位数
• 实际上一个元素就表示内存中的一个字节。
buffer中每一个元素的范围是从00 - ff 即0-225
计算机一个 0 或 1 称为1为(bit)
8 bit = 1 byte(字节)
1024 byte = 1 kb
1024 kb = 1mb
1024 mb = 1gb
1024 gb = 1 tb
var str = "Hello Atguigu";
将一个字符串保存到buffer中
var buf = Buffer.from(attr);
console.log(buf.length); 获取的是占用内存的大小	//13
console.log(str.length); 获取的是字符串的长度 //13
console.log(buf);	//<Buffer 48 65 6c 6c 6f 20 41 74 67 75 69 67 75>

	var buf = Buffer.alloc(10);
	创建一个10字节的buffer(即分配一个连续长度为10的空间)
	通过索引,来操作buf中 的元素
	var buf = Buffer.alloc(10); //alloc创造10个字节的空间同时会把数据清空
	buf[0] = 88;
	buf[1] = 255;
	buf[2] = 0xaa;
	buf[3] = 556;
	console.log(buf); 结果:<Buffer 58 ff aa 2c 00 00 00 00 00 00>
	【2c 是一个字节只有8个bit,溢出时只取后8位得出对应16制的2c】
	
	var buf1 = Buffer.allocUnsafe(10);//性能好,但可能泄露数据
	
console.log(buf1);//<Buffer 00 00 00 00 00 00 00 00 3f 00>

var buf = Buffer.from("郭大爷");
console.log(buf.toString());//数据转换成字符串

Buffer与字符串间的转换
– 支持的编码:
• ASCII、UTF-8、UTF-16LE/UCS-2、Base64、Binary、Hex

– 字符串转Buffer
• Buffer.from(str , [encoding]);

– Buffer转字符串
• buf.toString([encoding] , [start] , [end]);

写入操作
• 向缓冲区中写入字符串
– buf.write(string[, offset[, length]][, encoding])

• 替换指定索引位置的数据
– buf[index]

• 将指定值填入到缓冲区的指定位置
– buf.fill(value[, offset[, end]][, encoding])

读取操作
• 将缓冲区中的内容,转换为一个字符串返回
– buf.toString([encoding[, start[, end]]])

• 读取缓冲区指定索引的内容
– buf[index]

其他操作
• 复制缓冲区
– buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

• 对缓冲区切片
– buf.slice([start[, end]])

• 拼接缓冲区
– Buffer.concat(list[, totalLength])

Buffer.from(str) 将一个字符串转换为buffer
Buffer.alloc(size) 创建一个指定大小的Buffer
Buffer.alloUnsafe(size) 创建一个指定大小的Buffer,但是可能含敏感数据


把缓冲区的数据转换为字符串
buf.toString( )将缓冲区的数据转换为字符串

fs模块

– 1. 简单文件写入
– 2. 同步文件写入
– 3. 异步文件写入
– 4. 流式文件写入

–1. 简单文件读取
–2. 同步文件读取
–3. 异步文件读取
–4.流式文件读取

往一个文件中写入或读取大量数据时,最好的方法之一是使用流。

其他操作,用的很少


• 验证路径是否存在
– fs.exists(path,callback)
– fs.existsSync(path)
• 获取文件信息
– fs.stat(path, callback)
– fs.statSync(path)
• 删除文件
– fs.unlink(path, callback)
– fs.unlinkSync(path)
• 列出文件
– fs.readdir(path[, options], callback)
– fs.readdirSync(path[, options])
• 截断文件
– fs.truncate(path, len, callback)
– fs.truncateSync(path, len)
• 建立目录
– fs.mkdir(path[, mode], callback)
– fs.mkdirSync(path[, mode])
• 删除目录
– fs.rmdir(path, callback)
– fs.rmdirSync(path)
• 重命名文件和目录
– fs.rename(oldPath, newPath, callback)
– fs.renameSync(oldPath, newPath)
• 监视文件更改写入
– fs.watchFile(filename[, options], listener)

简单文件写入

fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])
参数:
– file 文件路径
– data 被写入的内容,可以是String或Buffer
– options 对象,包含属性(encoding、mode、flag)
– callback 回调函数
简单文件的写入

var fs = require("fs");
fs.writeFile("hello.txt","啦啦啦",{flag:"w"},(err)=>{
    if(!err)console.log("写入成功");
});

同步文件的写入

fs.writeSync(fd, buffer, offset, length[, position])
fs.writeSync(fd, data[, position[, encoding]])
要完成同步写入文件,先需要通过openSync()
打开文件来获取一个文件描述符,然后在通过writeSync()写入文件。

参数
– fd 文件描述符,通过openSync()获取
– data 要写入的数据(String 或 Buffer)
– offset buffer写入的偏移量
– length 写入的长度
– position 写入的起始位置
– encoding 写入编码
同步文件的写入

-手动操作的步骤
1.打开文件
 fs.openSync(path,flags[,mode])
    -path 要打开文件的路径
    -flags 打开文件要做的操作类型
        r 只读的
        w 可写的
    - mode 设置文件的操作权限,一般不传
     返回值:
     -该方法会返回一个文件的描述符作为结果,可以通过该描述符进行各种操作
    2.向文件中写入内容
    fs.writeSync(fd, string[, position[, encoding]])
    - fd 文件的描述符,需要传递要写入的文件的描述符
    - string 要写入的内容
    - position 表示起始的位置[一般不传]
    - encoding 写入的编码,默认utf-8
    3.保存并关闭文件
    fs.closeSync(fd)
    - fd 要关闭文件的描述符


var fs = require("fs");
//打开文件
var fd = fs.openSync("hello.txt","w");
//向文件中写入内容
fs.writeSync(fd,"love",4);
//关闭文件
fs.closeSync(fd);

异步文件的写入

fs.write(fd, buffer, offset, length[, position], callback)
fs.write(fd, data[, position[, encoding]], callback)
要使用异步写入文件,先需要通过open()打开文件,然后在回调函数中通过write()写入。
参数:
– fd 文件描述符
– data 要写入的数据(String 或 Buffer)
– offset buffer写入的偏移量
– length 写入的长度
– position 写入的起始位置
– encoding 写入编码

 	异步文件的写入
 
    异步文件打开
    fs.open(path[, flags[, mode]], callback)
    不会返回fd(文件的描述符),而是会在回调函数中显示,回调函数有两个参数fd,和err
    异步文件写入
    fs.write(fd, string[, position[, encoding]], callback)
    异步文件关闭
    fs.close(fd[, callback])


var fs = require("fs");
fs.open("hello.txt","w",function(err,fd){
    if(!err){ //如果没有出错,则继续向文档写入内容
       fs.write(fd,"我是hello.txt中的内容",function(err){
           if(!err){
               console.log("写入成功!!!");
           }
           fs.close(fd,function(err){
                if(!err){
                    console.log("文件关闭成功!!!");
                }
           });
       });
    }else{
        console.log("fail to open the file!");
    }
});

创建一个可写流

往一个文件中写入大量数据时,最好的方法之一是使用流。
若要将数据异步传送到文件,首需要使用以下语法创建一个Writable对象:

– fs.createWriteStream(path[, options])
path 文件路径
options {encoding:"",mode:"",flag:""}

一旦你打开了Writable文件流,就可以使用write()方法来写入它,
写入完成后,在调用end()方法来关闭流。

同步、异步、简单文件的写入都不适合大文件的写入,性能较差,容易导致内存溢出

var fs = require("fs");

流式文件写入
创建一个可写流

	fs.createWriteStream(path[, options])
		- 可以用来创建一个可写流
		- path,文件路径
		- options 配置的参数

var ws = fs.createWriteStream("hello3.txt");

//可以通过监听流的open和close事件来监听流的打开和关闭
/*
	on(事件字符串,回调函数)
		- 可以为对象绑定一个事件

	once(事件字符串,回调函数)
		- 可以为对象绑定一个一次性的事件,该事件将会在触发一次以后自动失效

* */
ws.once("open",function () {
	console.log("流打开了~~~");
});

ws.once("close",function () {
	console.log("流关闭了~~~");
});

//通过ws向文件中输出内容
ws.write("通过可写流写入文件的内容");
ws.write("今天天气真不错");
ws.write("锄禾日当午");
ws.write("红掌拨清清");
ws.write("清清真漂亮");

//关闭流,关闭写的那个文件的管口
ws.end();
//关闭流,关闭读的那个文件的管口

文件读取

和写入道理一样,改吧改吧就可以用

/*
	1.同步文件读取
	2.异步文件读取
	3.简单文件读取
	 fs.readFile(path[, options], callback)
	 fs.readFileSync(path[, options])
	 	- path 要读取的文件的路径
	 	- options 读取的选项
	 	- callback回调函数,通过回调函数将读取到内容返回(err , data)
	 		err 错误对象
	 		data 读取到的数据,会返回一个Buffer

	4.流式文件读取
 */


var fs = require("fs");

var path = "C:/Users/lilichao/Desktop/笔记.mp3";

fs.readFile("an.jpg" , function (err , data) {
	if(!err){
		//console.log(data);
		//data写入到文件中
		fs.writeFile("C:/Users/lilichao/Desktop/hello.jpg",data,function(err){
			if(!err){
				console.log("文件写入成功");
			}
		} );
	}
});


流式文件的读取

流式文件读取也适用于一些比较大的文件,可以分多次将文件读取到内存中

var fs = require("fs");

//创建一个可读流
var rs = fs.createReadStream("C:/Users/lilichao/Desktop/笔记.mp3");
//创建一个可写流
var ws = fs.createWriteStream("a.mp3");

//监听流的开启和关闭
rs.once("open",function () {
	console.log("可读流打开了~~");
});

rs.once("close",function () {
	console.log("可读流关闭了~~");
	//数据读取完毕,关闭可写流

	ws.end();
});

ws.once("open",function () {
	console.log("可写流打开了~~");
});

ws.once("close",function () {
	console.log("可写流关闭了~~");
});

//如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件,
data事件绑定完毕,它会自动开始读取数据
rs.on("data", function (data) {
	//console.log(data);
	//将读取到的数据写入到可写流中
	ws.write(data);
});


创建一个读写流


流式文件读取也适用于一些比较大的文件,可以分多次将文件读取到内存中

var fs = require("fs");

//创建一个可读流
var rs = fs.createReadStream("C:/Users/lilichao/Desktop/笔记.mp3");
//创建一个可写流
var ws = fs.createWriteStream("b.mp3");

//pipe()可以将可读流中的内容,直接输出到可写流中
rs.pipe(ws);

代码案例就不多写了,官网和其他网站有很多案例

BFF

BFF,即 Backend For Frontend(服务于前端的后端),
也就是服务器设计 API 时会考虑前端的使用,
并在服务端直接进行业务逻辑的处理,又称为用户体验适配器。
BFF 只是一种逻辑分层,而非一种技术,
微服务横行霸道之际,加入 BFF 就是为了聚合后端服务
BFF 的出现为前端应用提供了一个对业务服务调用的聚合点

IO是什么?

I/O(英语:Input/Output),即输入/输出,
通常指数据在内部存储器和外部存储器或其他周边设备之间的输入和输出。

同步

同步,就是调用某个东西是,调用方得等待这个调用返回结果才能继续往后执行
客户端发起请求后, 一直等到serve处理请求完成后才返回继续执行后续的逻辑

异步

异步,和同步相反  调用方不会理解得到结果,而是在调用发出后调用者可用继续执行
后续操作,被调用者通过状体来通知调用者,或者通过回掉函数来处理这个调用

阻塞非阻塞

  首先需要明白一个概念, Js是单线程, 但是浏览器并不是,
  事实上你的请求是浏览器的另一个线程在跑。

  如果是阻塞的话, 那么该线程就会一直等到这个请求完成之
  后才能被释放用于其他请求。

  如果是非阻塞的话, 那么该线程就可以发起请求后而
  不用等请求完成继续做其他事情。

同步异步不与阻塞非阻塞混为一谈

阻塞和非阻塞 强调的是程序在等待调用结果,当前线程会被挂起

同步和异步强调的是消息通信机制 

nodejs

阻塞 是指在 Node.js 程序中,其它 JavaScript 语句的执行,
必须等待一个非 JavaScript 操作完成。
这是因为当 阻塞 发生时,事件循环无法继续运行 JavaScript。

在 Node.js 标准库中的所有 I/O 方法都提供异步版本,非阻塞,
并且接受回调函数。某些方法也有对应的 阻塞 版本,名字以 Sync 结尾

阻塞 方法 同步 执行,非阻塞 方法 异步 执行

线程和进程

进程是 CPU资源分配的最小单位;线程是 CPU调度的最小单位
一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线
一个进程的内存空间是共享的,每个线程都可用这些共享内存

多线程和多进程

多进程:在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态
	比如你可以听歌的同时,打开编辑器敲代码,2个进程互不干扰


多线程:程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,
	也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
	比如渲染线程、JS 引擎线程、HTTP 请求线程等等

浏览器中的 Event Loop

Micro-Task 与 Macro-Task

浏览器的宏任务微任务
macro(宏任务)setTimeout、setInterval、script(整体代码)、 I/O 操作、UI 渲染等。
micro(微任务)new Promise().then(回调)、MutationObserver(html5新特性) 
node的宏任务微任务
macro-task 比如:setTimeout、setInterval、 setImmediate、script(整体代码)、 I/O 操作等。
micro-task 比如: process.nextTick、new Promise().then(回调)等。

浏览器的eventloop

原链接

node的eventloop

在这里插入图片描述


外部输入数据-->轮询阶段(poll)-->检查阶段(check)-->关闭事件回调阶段(close callback)
-->定时器检测阶段(timer)-->I/O事件回调阶段(I/O callbacks)-->闲置阶段(idle, prepare)
-->轮询阶段(按照该顺序反复运行)

timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
idle, prepare 阶段:仅node内部使用
poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
check 阶段:执行 setImmediate() 的回调
close callbacks 阶段:执行 socket 的 close 事件回调

(1) timer
timers 阶段会执行 setTimeout 和 setInterval 回调,并且是由 poll 阶段控制的。 
同样,在 Node 中定时器指定的时间也不是准确时间,只能是尽快执行
(2) poll
poll 是一个至关重要的阶段,这一阶段中,系统会做两件事情

1.回到 timer 阶段执行回调

2.执行 I/O 回调

并且在进入该阶段时如果没有设定了 timer 的话,会发生以下两件事情

如果 poll 队列不为空,会遍历回调队列并同步执行,直到队列为空或者达到系统限制
如果 poll 队列为空时,会有两件事发生
如果有 setImmediate 回调需要执行,poll 阶段会停止并且进入到 check 阶段执行回调
如果没有 setImmediate 回调需要执行,会等待回调被加入到队列中并立即执行回调,
这里同样会有个超时时间设置防止一直等待下去
当然设定了 timer 的话且 poll 队列为空,则会判断是否有 timer 超时,
如果有的话会回到 timer 阶段执行回调。


(3) check阶段
setImmediate()的回调会被加入check队列中,从event loop的阶段图可以知道,
check阶段的执行顺序在poll阶段之后
console.log('start')
setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(function() {
    console.log('promise1')
  })
}, 0)
setTimeout(() => {
  console.log('timer2')
  Promise.resolve().then(function() {
    console.log('promise2')
  })
}, 0)
Promise.resolve().then(function() {
  console.log('promise3')
})
console.log('end')
brower
//start=>end=>promise3=>timer1=>promise1=>timer2=>promise2
node
//start=>end=>promise3=>timer1=>timer2=>promise1=>promise2
(4) setTimeout 和 setImmediate
setImmediate 设计在poll阶段完成时执行,即check阶段;
setTimeout 设计在poll阶段为空闲时,且设定时间到达后执行,但它在timer阶段执行
setTimeout(function timeout () {
  console.log('timeout');
},0);
setImmediate(function immediate () {
  console.log('immediate');
});

对于以上代码来说,setTimeout 可能执行在前,也可能执行在后。
这是由源码决定的进入事件循环也是需要成本的,
如果在准备时候花费了大于 1ms 的时间,那么在 timer 阶段就会直接执行 setTimeout 回调
如果准备时间花费小于 1ms,那么就是 setImmediate 回调先执行了
但当二者在异步i/o callback内部调用时,总是先执行setImmediate,再执行setTimeout
但当二者在异步i/o callback内部调用时,总是先执行setImmediate,再执行setTimeout
const fs = require('fs')
fs.readFile(__filename, () => {
    setTimeout(() => {
        console.log('timeout');
    }, 0)
    setImmediate(() => {
        console.log('immediate')
    })
})
// immediate
// timeout

在上述代码中,setImmediate 永远先执行。
因为两个代码写在 IO 回调中,IO 回调是在 poll 阶段执行,当回调执行完毕后队列为空
,发现存在 setImmediate 回调,所以就直接跳转到 check 阶段去执行回调了。
(5) process.nextTick
setTimeout(() => {
 console.log('timer1')
 Promise.resolve().then(function() {
   console.log('promise1')
 })
}, 0)
process.nextTick(() => {
 console.log('nextTick')
 process.nextTick(() => {
   console.log('nextTick')
   process.nextTick(() => {
     console.log('nextTick')
     process.nextTick(() => {
       console.log('nextTick')
     })
   })
 })
})
// nextTick=>nextTick=>nextTick=>nextTick=>timer1=>promise1

node学习体系

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值