前言
NodeJS这东西是不是学过了,之后感觉又像没学到什么东西???
我最近翻到了之前学习node的笔记,又结合了一些大佬的经验,这里把node系列相关的东西串联一下,分享给小伙伴们,顺便我自己也加深一下印象。
总共分为六篇
node打怪升级系列 - 基础篇
node打怪升级系列 - Koa篇
node打怪升级系列 - 浅谈require函数
node打怪升级系列 - 手写中间件篇
node打怪升级系列 - 手写发布订阅和观察者篇
node打怪升级系列 - 手写compose(洋葱模型)
node打怪升级系列 - 手写简易脚手架篇
本文重点记录下基础篇
里的基础知识、事件循环机制等装备
正文开始
1,声明式编程和命令式编程
学NodeJS之前,得换一下编程思维,Node
是用命令式编程~~~
前端的编程更倾向于声明式编程
,比如大家耳熟能详的Vue
后端的编程更倾向于命令式编程
,比如Java
, Node
1.1,声明式编程
大白话:去餐厅吃饭,告诉服务员给你上一份北京烤鸭
,过一会烤鸭就给你上来了,你不需要关心烤鸭咋做的。
声明数据和一个按钮的逻辑关系,当数据变化时,按钮的状态也会随之变化。开发者并不需要告诉Vue如何具体地去更新这个按钮,只需要声明数据和按钮的关系即可。
js
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
}
}
};
</script>
1.2,命令式编程
大白话:跟着菜单一步步
学做菜,首先热锅,然后倒油,然后放葱姜蒜....,最后做出来一道菜。(不要钻牛角尖,油还需要大豆去炼制,才能得到)
js
class Test {
public static void main(String[] args) {
// run ...
}
}
总的说,命令式编程更接近本质,声明式编程更接近上层业务。
2,node是什么?
node是单线程吗?这算是一个错误的问题。
Node
就是Node.js的简称,它是一个运行环境/宿主环境
。Node是基于CommonJS规范。
2.1,宿主环境
宿主环境这个词不知道小伙伴陌生不陌生~~~
平时我们做网页开发的时候,承载网页的宿主环境
就是浏览器(Chrome为例)。
宿主环境提供了很多API
供开发使用。
2.2,宿主环境和V8引擎的关系
Node和浏览器这两个宿主环境对代码的解析执行都用到了V8引擎
下面看下这两个console.log的执行的过程
js
console.log('hello world')
console.log(document.getElementsByTagName('head')[0]);
解析:
-
V8引擎开始进行解析
-
通过词法分析和语法分析转成AST(抽象语法树)
-
根据AST得知console 是一个对象,log 是一个函数,hello world 是一个字符串,作为 log 的参数
执行:
- V8引擎解析完以后是否可以执行,是宿主环境决定的,如果支持,宿主环境会配合V8引擎对
console
进行打印
上面的hellow world
在浏览器
和Node
这两个宿主环境
中都可以打印
但是document.getElementsByTagName
在Node
中执行会报错,因为Node
这个宿主环境没提供document API
2.3,Node API
写代码的本质就是操作宿主环境的API
~~~
Node提供的API主要有:
-
操作磁盘:
fs
,path
-
处理网络:
http
,net
-
多进程:
cluster
-
操作系统:
os
-
加密:
opensssl
-
压缩:
zlib
-
...
此处顺便把浏览器提供的API也说下吧:
-
处理和显示dom:
document
-
窗口:
window
-
屏幕:
screen
-
...
3,buffer
是一个固定长度的缓冲区,对于处理图片、接收文件上传等操作二进制数据时用到的一个东西,用于操作字节。
小伙伴们有个概念就行,常规的业务不太能用到。小伙伴可以收藏起来,先跳过,以后再看
过大的文件,可以用buffer分片传输
,可以用stream,stream更合适
3.1,运行流程
以接收文件为例,大致流程:File -> Buffer 的缓冲区 -> wait 进程再去处理
File 到 Buffer:
- 接收读取一个文件时,文件的内容会被读取到Buffer中,用于临时存储数据。
缓冲区(Buffer) :
- 缓冲和暂存数据,使得数据的读取和写入操作更加高效,等待进一步处理。
wait 进程再去处理:
- 当系统资源空闲时,由一个进程来统一
异步处理
处理这些数据。
3.2,Buffer的使用
基础使用
buf2.copy(targetBuffer, targetStart, sourceStart, sourceEnd);
js
// <Buffer e5 b0 98 e8 90 bd> 尘落有六个字节
const bufStr = Buffer.from('尘落'); // 获取字符串的字节
const new_buf = Buffer.alloc(12); // 分配12个字节的内存。
bufStr.copy(new_buf, 0, 0, 6);
bufStr.copy(new_buf, 6, 0, 6);
bufStr.copy(new_buf, 0, 0, 6);
把尘落的6个字节放到new_buf的前
6个字节上
bufStr.copy(new_buf, 6, 0, 6);
把尘落的6个字节放到new_buf的后
6个字节上
读文件的二进制字节
js
const fs = require('fs');
const path = require('path');
let buf = Buffer.alloc(30);
fs.open(path.resolve(__dirname, './a.js'), 'r', function (err, rfd) {
fs.read(rfd, buf, 0, 30, 0, function (err, bytesRead) {
console.log(rfd, buf);
})
});
4,事件循环
浏览器和Node的事件循环是不一样的,Node事件循环每次循环前要先执行完同步代码、process.nextTick队列
大白话:Node中的事件循环就是同步代码
微任务
宏任务
process.nextTick
setImmediate
的执行顺序
优先级:同步队列 > process.nextTick队列 > 微任务队列(Promise队列) > 宏任务(timers队列) > setImmediate(check队列)
Promise队列指的是Promise.then()等微任务队列, new Promise属于同步队列
下面这段代码的执行顺序理清楚,Node事件循环就可以过了
js
async function async1() {
console.log('1'); // 2
await async2();
console.log('2'); // 8
}
async function async2() {
console.log('3'); // 3
}
console.log('4'); // 1
setImmediate(() => {
console.log('5'); // 11
})
setTimeout(() => {
console.log('6'); // 10
setImmediate(() => {
console.log('7'); // 12
})
}, 0);
async1();
process.nextTick(() => {
console.log('8'); // 7
})
new Promise((resolve) => {
console.log('9'); // 4
resolve();
console.log('10'); // 5
}).then(() => {
console.log('11') // 9
});
console.log('12'); // 6
执行顺序光看上面的代码能理清楚吗?
哈哈, 这里贴下中文解释
第一次循环
循环前执行完同步代码
、nextTick队列
、Promise队列
- 执行同步代码,执行nextTick队列(为空),Promise队列(为空)
- 打印的内容
console.log('4')
console.log('1')
console.log('3')
console.log('9')
console.log('10')
console.log('12')
执行代码,压入队列
process.nextTick队列
- console.log('8')
Promise队列
- console.log('2'), console.log('11')
- console.log('2')是因为
await async2()
所以被压入了微任务队列
- console.log('2')是因为
timer队列
- setTimeout => "console.log('6')", "setImmediate(() => {console.log('7');})"
check队列
- setImmediate => console.log('5');
第二次循环
循环前执行完同步代码
、nextTick队列
、Promise队列
- 执行nextTick队列,打印的内容
console.log('8')
- 执行Promise队列,打印的内容
console.log('2')
,console.log('11')
执行timer队列
- 执行timer队列过程中发现里面有其他setImmediate插入check队列末尾
- 打印的内容
console.log('6')
此时check队列
- "setImmediate => console.log('5')", "setImmediate(() => {console.log('7');})"
执行check队列
- 打印的内容
console.log('5')
,console.log('7')
5,node可以做什么
这里罗列了一个大佬的回答~~~
-
前端开发: webpack, rollup
-
跨端开发: weex, RN
-
后端开发: koa, express, egg
-
工具开发: 脚本, 脚手架,命令行
脚手架
和命令行
的文章后面也会更新~~~
完结
这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。
欢迎转载,但请注明来源。
最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。