⚡node打怪升级系列 - 基础篇

前言

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.getElementsByTagNameNode中执行会报错,因为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()所以被压入了微任务队列

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

  • 工具开发: 脚本, 脚手架,命令行

脚手架命令行的文章后面也会更新~~~

完结

这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。

欢迎转载,但请注明来源。

最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。

相关推荐
腾讯TNTWeb前端团队4 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom9 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom9 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom9 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom9 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试