跳到主要内容

框架对比

Vue3 / Svelte / React 的 diff

(1)React:基于虚拟 DOM + Fiber 架构

更新原理:每次更新时,有调度、协调、渲染三个阶段。在调度阶段会根据任务优先级把工作切片,在协调阶段不在生成完整的虚拟 DOM 对象,而是直接在 Fiber 节点上保存 render 函数输出。每次更新都会遍历旧 Fiber Tree,同步构造新 Fiber 节点并比较上一轮的 alternate 节点生成最小的变更列表(effect list),再统一渲染到真实 DOM。

  • 优点:实现了异步可中断更新 + 优先级调度,通过时间切片分割成子任务,随时将控制权交还给浏览器。以减少线程的长期占用,造成页面卡顿。
  • 缺点:需要维护虚拟 DOM 创建,如果一次更新有大量 render,或深层次组件,则性能受损。

(2)Vue:虚拟 DOM(VNode)+ 模板 AST 优化

更新原理:通过响应式系统追踪依赖,在数据变化时重新生成虚拟 DOM(VNode),与上一次的 VNode 进行 diff,最后将变更部分 patch 到真实 DOM。

  1. 响应式系统,依赖追踪。通过 getter 收集依赖 / setter 通知依赖更新。
  2. 当某个组件需要更新时,会加入到更新队列中;更新队列会在 nextTick 异步合并改动,批刷新;
  3. 在 flush 刷新时,会调用待更新组件的 render 函数,得到新的 VNode 虚拟 DOM。
  • 优点:「模板驱动」,语法的限制可以让 vue 静态时预优化,减少 diff。
    1. patchFlag:Vue 可以在编译阶段静态分析,给发生变更的 VNode 打 patchFlag,更精细的知道是 class、还是 title 等哪些 props 发生变化,在渲染时只更新有变化的部分。
    2. 双端指针 + 最长递增子序列:尽量复用元素,减少 v-for 列表发生变化是的 VNode 调整。
  • 缺点:diff 和渲染过程是同步不可中断,必须是同步递归的执行 diff。

(3)Svelte:无虚拟 DOM,编译时 diff

Svelte 是模板编译器,而不是运行时框架。

更新原理:编译时优化、静态依赖分析、精确 DOM 操作。

  1. 编译时追踪依赖关系:在编译阶段完成模版解析和依赖收集。语法需要显式告知依赖关系,Svelte 在编译阶段就知道哪个 DOM 节点依赖,完全没有虚拟 DOM 或 diff,全靠状态追踪生成最小更新指令。
  2. 编译为最小更新指令:和 Vue/React 渲染再 diff 的做法不同,Svelte 把更新逻辑编译为明确 DOM API 指令,没有 DOM Tree 递归比较,没有 diff 过程。
  • 优点:diff 不是运行时行为,而是编译期分析。静态分析模板结构、绑定变量关系,生成精确的 DOM 更新指令,无需虚拟 DOM、无需 diff,避免了运行时开销。
  • 缺点:编译成最小 DOM 操作指令,会造成打包体积膨胀的问题。每个组件都存在私有代码,而不是统一调度,无法复用。但它没有 dif 又节约了调度器的体积。

所以,如果是小型组件上使用 svelte,体积是很极致的。但在大型项目上打包体积会变大。

函数式编程 / class 编程区别

函数式编程(Functional Programming) 与 面向对象编程(OOP / Class-based Programming) 的区别。

特性对比函数式编程(Functional)面向对象编程(OOP / Class)
核心理念把计算建模为函数的组合;数据不可变、无副作用把系统建模为对象,封装状态与行为
关注点数据的转换过程(输入 → 输出)对象的状态与行为(对象的内部状态与方法)
状态管理倾向于无状态,数据不可变强调封装、对象内部状态管理
代码组织方式用纯函数 + 高阶函数组合复杂逻辑用类、继承、接口组织逻辑结构
扩展性倾向组合(composition)倾向继承(inheritance)
可测试性强:无副作用,易测试弱:依赖对象状态和方法,测试更复杂
可读性简洁但存在函数嵌套、闭包链接近现实建模,结构清晰,逻辑封装更强

具体场景:

  1. 函数式编程常用于
    • 数据转换:.map().filter(),通过高阶函数或组合式 API,封装行为;
    • 优点:易组合复用,副作用链清晰,容易单元测试;
    • 缺点:多层嵌套时可读性变差,组件多个生命周期(内部状态行为)时,不容易体现;
  2. Class 编程常用于
    • 使用装饰器 / 继承行为(NestJs);
    • 优点:逻辑集中,代码组织清晰;符合现实世界抽象(面向对象);
    • 缺点:继承逻辑过深时,变得不易追溯和调整;状态副作用不清晰,可测试性差;

总结:应当函数式为主,Class 辅助

  • 函数式编程:强调 “行为分离与组合”,适合表达转换型逻辑无状态流程

  • 面向对象:更适合状态封装、角色建模与模块化组织。

元编程的理解

元编程:“操作语言本身” 或其执行行为。通过对元编程,对代码的运行时表现进行调整,比如:

  • 响应式:Proxy + Reflect 或 Object.defineProperty,劫持属性修改和访问的行为;
  • 微前端:使用 eval() / with() ,配合 Proxy 进行子应用的作用域隔离;
  • AOP 面向切面:NestJs 装饰器 Decorator + Reflect Metadata 对装饰对象的元信息读写;
  • 方法重写:拦截代理网络请求 / 定时器等,实现日志记录或性能监控;

微前端

qiankun 的隔离机制

子应用之间需要隔离:

  1. 全局 Window、公共 JS 变量冲突、CSS 样式污染、事件/ 定时器及时清理

JS 隔离思路:

  1. 默认使用 Proxy 沙箱,也就是子应用处在代理 window 对象中。当子应用访问 window 时,实际通过 getter 访问的是代理的 window 环境。
    • qiankun 通过 eval 执行子应用的脚本,注入代理的 window / globalThis,让子应用在 proxy 作用域下执行。
  2. 但 Proxy 不兼容低版本 IE。于是有 Snapshot 快照沙箱,当前挂载的子应用如果在 window 上登记全局变量,就会被记下。
    • 在挂载子应用时,会浅拷贝一份当前 window,然后把上一次的快照应用到 window 上;
    • 在卸载子应用时,会先 diff 下当前 window 和快照的区别,作为下次恢复子应用的依据同时删除新增的全局变量,恢复环境。
    • 缺点:只会有一个快照变量保存,在 window 保存快照和恢复状态时,可以读取到 window 上的脏数据。同时不允许有两个子应用并存。
  3. iframe
    • 天然隔离 widnow、CSS 样式、全局状态等。但每个 frame 有独立的上下文环境,性能会降低,同时主子应用通信困难。

网络请求隔离:使用 fetch 代理所有网络请求,以确定是哪一个子应用发起的请求。

CSS 隔离:Shadow DOM,或者给子应用的 CSS 样式全部增加前缀。

  • 子应用渲染在一个带有 Shadow DOM 的容器内,隔离了 DOM 环境。

qiankun 的缓存机制

如果开启 keep-alive,流程是:

  1. 第一次访问子应用:拉取 HTML 文件(纯字符串),解析内容,拉取相关的 CSS、JS 等资源;
    • 等待基座触发子应用 mount() 时,才会在沙箱中执行这些资源。具体来说,如果是 Vue,就会执行暴露的 createApp 方法,挂载整个页面。
    • 在切走子应用时,会触发 umount() 生命周期,执行暴露给子应用基座的卸载方法。最后完全销毁 DOM、事件、沙箱。但是会缓存 JS 执行的模块定义、CSS 解析后的样式规则。
  2. 再次访问子应用时:不会重复拉 HTML 以及相关的资源,因为有缓存,会重新 mount()
    • 如果有 SDK 脚本,不会重复下载和执行,全局变量和对象会被清除,需在 mount 中重新调初始化逻辑。

子应用通信 / window 对象

  1. 在基座中注册子应用时,可以传递 props。props 中增加一个写入方法,子应用调;
  2. Actions 通信 (推荐),基座定义 initGlobalState 公共变量区,子应用可以在 props 拿到并读写;
  3. 自定义事件,子应用触发一个 CustomEvent,并附带参数;基座实时监听,并处理参数。
  4. 在非严格沙箱环境下,基座可以在注册子应用时,增加 globalVarWhiteList 全局变量访问白名单,子应用可以直接 window.xxx 访问这些变量。
  5. PostMessage:iframe 的通信方式。获取到对方 window 变量后,进行 postMessage,对方监听

Vue

Vue2/3 响应式

  • Vue 2 通过 Object.defineProperty 劫持每个响应式属性的 getter 和 setter,实现依赖收集与派发更新;
    • 局限性:
      • 新增/删除属性无法被监听,vue 只能完整的重新劫持对象,无法按需劫持;
      • 响应式缺陷,如 length 等一些变更方法无法触发更新,只对特定的数组方法进行劫持;
      • 对象内所有属性都需劫持,深层嵌套对象需要递归劫持,性能较差,同时依赖更新时可能导致整个组件更新。
  • Vue 3 用的是 Proxy 实现响应式代理,可以深层递归、动态追踪所有对象操作,性能更优。
    • 使用 Proxy 拦截对象访问,通过 track 收集依赖、trigger 派发更新,构建了一个细粒度、按需追踪的响应式系统。
    • 解决了:
      • 动态监听任意属性:不需要提前劫持,可以响应后续添加的属性;
      • Proxy + Reflect 支持数组方法、Map、Set 等完整的数据操作拦截;
      • 更新粒度不再是整个对象,而是可以监听到具体哪个子属性存在依赖关系;
      • 只有在产生依赖关系时,才懒劫持、惰性创建依赖关系,而不需在定义对象时完整的监听。

Vue3 的响应式

  1. 创建响应式对象:调用 reactive() 时,会返回一个 Proxy 对象,内部封装了 getter / setter 两个拦截器。
    1. 访问属性时,会触发 getter,进而调用 track 进行依赖收集,记录当前的副作用函数;
    2. 修改属性时,会触发 setter,进而调用 trigger,查找所在依赖这个 target 的副作用函数,然后依次触发这些函数的重新执行。
  2. 执行副作用函数:调用 effect(() => state.count) 时,会立即执行内部函数,并标记为 currentEffect。
    1. 在执行过程中,访问了响应式对象的属性,会触发调用 gettertrack ,然后收集当前的副作用函数。放在,targetMap 全局依赖图,是一个 WeakMap 用于收集和查找依赖。
    2. 当依赖的属性变更时,则调用 settertrigger ,调用这个副作用。
  3. 副作用函数中,不同于 React,存在 if/else 等逻辑,关系是动态变化的。
    • 所以在每次执行 effect 之前,都会清空之前收集的依赖,重新收集本次访问的依赖。

Vue2/3 区别

  1. 上文的响应式区别;
  2. 基于响应式的实现不同,Vue3 可深度监听对象的操作,有可精确到字段的依赖追踪(Vue2 只有组件为单位);
    • Vue3 可以对虚拟 DOM 的 diff,在编译阶段优化,通过 PathFlag 可构建更小影响范围的副作用链。
  3. 逻辑复用的方式:Vue2 使用 mixins / extends 继承,易有命名冲突等问题;Vue3 使用 composition API + Hooks。
  4. 语法组织形式:Options API 写法割裂;setup() 函数入口,函数式组织更灵活;

vue 双向数据 v-model

是 Vue 的语法糖,背后是 prop + event,依靠响应式系统(getter/setter 或 Proxy)。 本质是把一个外部值传进来(prop),又通过事件将用户修改同步出去(emit)。

computed 在 react 的替代

  • computed 是基于响应式依赖自动追踪的 “缓存计算属性”,只有依赖项变化时才重新计算,否则返回缓存值。
  • React 使用 useMemo() 实现同样的缓存功能,只是它需要额外在增加依赖数组,没有实现自动化的响应式追踪机制。

vue 编译原理

编译阶段:把 template 编译为 render 函数

  1. 解析阶段 Parse:把 HTML 模板字符串转换成一棵 AST 抽象语法树,保留标签、属性等信息;
  2. 优化阶段:根据依赖关系,标记 PatchFlag 更新点,用于后续跳过不必要的 diff;
  3. 生成阶段:将 AST 转化为可执行的渲染函数代码(字符串),最终会返回一个 render 函数;

运行阶段:

  1. 执行 render 函数,生成虚拟 DOM(VNode );
  2. 与上次的 VNode 进行 diff 比较;根据差异生成 patch 操作,更新真实 DOM。

vue 组件逻辑复用方式

Vue 中常见的组件逻辑复用方式包括:

  1. mixins:混入多个逻辑对象,属性合并;
    • 多个 mixin 的 data、methods、hooks 会在组件创建时合并到组件本身。
    • 命名冲突不可见,继承来源不清晰,不利于维护。
  2. extends:继承单一逻辑对象,单一来源扩展;
    • 扩展性较差。
  3. Vue 3 推荐:组合式 API(Composition API) 和 自定义 hooks(可复用函数)。
    • 自定义 hook 可将 props、methods 抽离拆分,复用逻辑。

组件封装思路

提取可复用 UI + 逻辑 + 状态,对外暴露清晰的 props/slots/events 接口,对内保证灵活可控、可维护、可拓展。

vue 两种路由模式

  • hash 模式基于 location.hash 实现,兼容性好无需服务端支持;
    • 使用 URL 中的 # 锚点,来模拟路径变化,对服务器透明。
    • 通常是同一个 SPA 内进行路由跳转。
  • history 模式基于 HTML5 History API,更美观但依赖后端配置。
    • 如果用户刷新页面,增加的跳转参数就会传给后端,后端需支持对应数据的拉取。

NestJs

NestJS 核心思想

NestJS 借助面向对象 + 函数式编程 + TypeScript 装饰器 + IOC/AOP 模型,实现了高度可扩展的、模块化的 Node.js 应用框架。

  1. IOC(控制反转)+ DI(依赖注入)
    • 核心是通过装饰器 + 元数据反射 来注册类及其依赖
    • @Injectable() 标记服务为可注入,Nest 的容器(IoC Container)负责实例化和注入
    • 控制权从开发者的手动 new,转为框架自动管理(解耦、可测试、可替换)
    • 底层实现:
      • Nest 的 IoC 容器(内部维护一个 token → provider 的映射);
      1. 装饰器 @Injectable, @Controller:会将 构造函数传递给装饰器中,以此能获取构造函数需要的入参,获取依赖关系。
      2. 装饰器内用 Reflect.getMetadata():用户对 A Class 注入 token,Nest 可以获取构造函数的参数类型(获取元属性),然后递归实例化每个依赖,并注入 A Class 中,实现增强。
  2. AOP(面向切面编程)
    • 通过 拦截器(Interceptor)、守卫(Guard)、管道(Pipe)、异常过滤器(Filter) 实现通用逻辑的抽离与复用;
    • 典型场景:权限控制、日志埋点、统一错误处理、响应封装。
    • 底层实现:在函数调用前后,通过对 Controller 中方法的高阶包装,进行调用前后的拦截。
  3. 模块化架构(Module)
    • 每个业务逻辑单位(如 controller、service 等)被整合在一个 @Module() 中。
    • 支持模块之间导入/导出 Provider,遵循单一职责、封装边界清晰。

核心工具 / 执行顺序 / 面向切面编程

执行顺序,请求处理的完整流程

  1. 请求阶段:中间件 → 守卫 → 拦截器 → 管道;
  2. controller:控制器方法处理 handle;
  3. 响应阶段:拦截器
  4. 发生异常:异常过滤器

面向切面编程

  1. 中间件:请求会被 middleware 处理,这一层可以复用 express 的中间件生态。可以是 class 实现,也可以注入其他 provider。
    • 适用于:处理原始 HTTP 请求
      • 日志记录:记录所有请求的  IP、方法、URL  和响应时间
      • 身份验证基础设施:解析 JWT token,设置 req.user,解析 session 等。
  2. 守卫:接下来会经过 Guard 的处理,它可以通过上下文对象 ExecutionContext 拿到目标 class、handler 的 metadata 等信息,可以实现权限验证等功能。
    • 适用于:基于条件的访问控制
      • 权限控制:检查用户是否有权访问特定资源
      • 角色验证:验证用户是否拥有执行操作的角色
  3. 拦截器:Interceptor 在请求前/后做处理,可通过 上下文对象 ExecutionContext 拿到 class、handler 信息。
    • 适用于:转换请求/响应,添加横切逻辑
      • 响应转换:统一响应格式为 {status, message, data}
      • 缓存:将耗时操作(数据查询)的结果临时存储起来,当相同请求再次到来时,直接返回缓存的结果,而不必重新执行操作。
  4. 管道:在到达 handler 之前,还会对参数用 Pipe 做下检验和转换。
    • 适用于:数据转换和验证
    • 参数验证:验证 request 参数是否正确;
    • 数据转换:将 string 参数转换为 number 等;
  5. handler:真正的逻辑处理,调用 controller 中的函数去解决;
  6. 拦截器:Interceptor 在请求后处理逻辑;
  7. 异常过滤器:在任何位置抛出异常,都会用 Exception Filter 处理,返回统一的响应信息。

Express、Koa、NestJS

特性ExpressKoaNestJS
1. 中间件模型(req,res,next) 链式调用async (ctx,next) 洋葱模型装饰器驱动 + 多模块化管道(middleware, guards, pipes, interceptors, filters)
2. Context 对象分散:req/res 分离ctx 聚合(封装 req/res)ExecutionContext 抽象,封装 req/res 适配多平台(HTTP、WebSocket、gRPC)
3. 异步语法早期 callback;支持 promise/async原生 async/await 设计完整 async/await,结合 RxJS(可选)
4. 类型支持JS 原生;TS 靠社区类型声明同 Express原生 TypeScript

Context 上下文对象

  • 是框架在处理请求时创建的一个中间层封装,用来统一访问 HTTP 请求(req)和响应(res)相关数据和方法,并在中间件链/控制器中传递。

它封装了:

  • 请求的参数访问:query, body, params, headers;
  • 响应的输出内容:status, body, setHeader;
  • 中间件共享的数据;
  • 平台无关的封装(如 HTTP or WebSocket)。

三个平台的对比:

  1. Express:没有共享的 context 对象。而是使用拆分的 req 请求体 + res 响应体。
  2. Koa:有 ctx 共享对象,聚合了请求、响应、共享上下文等信息,可在不同中间件传递。
  3. NestJs:封装了通用上下文对象 ExecutionContext,适配不同平台 HTTP、RPC、WebSocket。并且可以通过装饰器注入 req / Res 对象,通过 getHandler 获取元信息,支持复杂业务场景。

通识

进程和线程

  • 进程解决的是「程序隔离资源分配」的问题,

  • 线程解决的是「并发执行资源共享」的问题。

进程:程序之间需要隔离运行,互不干扰;系统为每个程序分配独立资源,确保安全性、健壮性。

  • 是程序在操作系统中的独立运行单位,可随时调度单个进程:挂起、终止、恢复,而整个系统不受影响;

线程:一个进程中需要同时做很多事(并发要求),同时有资源共享的需求。

  • 是进程内的最小执行单元,支持并发执行。

一个进程有 1 个堆+n 个栈

一个进程内通常有:一个堆(Heap),多个栈(Stack)

  • 1 个堆:用于存储动态分配的内存,如对象、闭包等。进程级共享,由内存管理器统一分配与释放。
    • 由运行时(如 C/C++、JS 引擎)动态管理;
    • 在 JavaScript 中,对象、闭包等引用类型都在堆中分配;
    • 不同线程可访问堆中对象(需要同步机制如锁)
    • 生命周期由程序或 GC 控制,大小远大于栈。
  • n 个栈:每个线程有一个独立的栈,用于函数调用帧、局部变量、返回地址等,栈空间随线程生命周期存在。
    • 用于:存储函数调用栈、局部变量(基本类型)、返回地址/参数;
    • 自动分配/释放,速度快,空间小。

🌈 区分设计原因

  1. 分离职责,便于管理和优化
    • 堆:动态内存,生命周期不定,如对象/闭包/DOM 节点,需要 GC 进行回收;
    • 栈:函数调用过程、局部变量、参数传递,根据调用情况先进后出,自动回收;
    • 栈是结构化访问,可以高效分配和释放;堆是任意访问,需要灵活分配,不易回收
  2. 多线程下的隔离性与并发性
    • 每个线程都维护自己的调用栈,确保线程间执行顺序不受干扰;
    • 线程间也需要快速的共享和通信,使用堆解决访问问题,但需要锁;
  3. 是性能与灵活的权衡
    • 栈:私有变量,地址分配快,访问速度快,体积小,可预测的生命周期;
    • 堆:共享变量,相反。

多个线程线程共享一个堆,实现数据共享,而各自持有独立栈,保证线程安全与调用效率。

Node 概述

事件循环:🔗

Node.js 是一个基于 V8 引擎的 JavaScript 运行时,使用 libuv 提供跨平台的异步 I/O 能力,通过模块系统和事件循环机制构建起高并发、高可用的服务端运行架构。

image-20250722000502959

概述:

JavaScript 应用层应用代码、业务逻辑
Node.js Runtime 层提供模块系统、fs/net 等 API(封装 V8 + libuv 能力)
V8 引擎解释并运行 JS
libuv 异步 I/O 层提供异步 I/O、事件循环、线程池等核心运行能力
操作系统层最终执行实际的文件、网络、内存、线程调用

🍊 整体结构:

第一层:Javascript 应用层

  • 用户编写的 js 代码,这些 JS 代码会被交由 V8 引擎执行。

第二层:V8 引擎(JavaScript 引擎)

负责解释、编译并执行 JavaScript 代码。运行环境中提供了 JS 的基本功能(如 Function, Object, Array, Promise)

  • 管理:作用域 / 闭包、内存分配、垃圾回收(GC)、异常处理
  • V8 不包含 fs/http 等功能,它只执行 JS 语法

第三层:Node.js Runtime 层(Node 提供的 API 能力)

提供完整的服务端运行环境,暴露了调用操作系统底层的 Node API,包括:

  1. 模块系统(Module System)
    • 支持 CommonJS(require / exports) 和 ESM(import / export)
    • 实现模块加载、作用域隔离、缓存等功能
  2. 内置模块(Built-in Modules)
    • Node 用 C++/JS 实现了系统级模块:fs, http, net, os, process, timers, stream;
    • 有些模块是用 JS 包装的,有些直接调用原生 API
  3. JS 与 C++ 的绑定层(Binding)
    • 将 C++ 实现的模块通过绑定函数暴露给 JS;
    • 让 JS 能调用底层,例如:fs.readFile() → 调用 C++ 实现 → libuv I/O → 操作系统

第四层:libuv(Node 的异步 I/O 引擎)

Node 的异步特性依赖 libuv 实现,包括 Promise 背后的任务调度也是基于事件循环实现的。

Node 的核心调度器,提供:

  • 异步事件循环(Event Loop)、消息队列;
  • 文件/网络等 I/O 任务的回调调度;
  • 线程池,多个线程的执行和调度(如文件读取、压缩解压等耗时操作);
  • 定时器控制(实现 setTimeout 等);

第五层:操作系统 / 系统调用层

libuv 封装了不同平台的系统调用接口,提供统一抽象给 Node 使用。最终所有的操作都会调用操作系统提供的底层 API,如:

  • Linux 的 epoll、macOS 的 kqueue、Windows 的 IOCP;
  • 文件系统 API:open, read, write;
  • 网络套接字:TCP, UDP, DNS;
  • 多线程 API:pthread, CreateThread 等;

进程 / 线程间通信

  • 进程通信靠操作系统(慢但安全),线程通信靠共享内存(快但复杂)。

(1)进程间通信:

不同进程之间无法直接共享内存,需要借助操作系统提供的 IPC 机制来通信。

常见通信方式:

  1. 命名管道。通过文件路径识别管道;
  2. 消息队列。操作系统内核维护的消息缓冲区;
  3. 共享内存。多个进程共享某一块物理内存区域,最快,但需要同步机制(信号量);
  4. 套接字 Socket:可进行远程进程间通信,比如计算机网络的 TCP/IP 层。

(2)线程间通信:

线程共享同一个进程内存空间,可以直接访问彼此的数据,因此通信效率高。

常见通信方式:

  1. 共享变量:所有线程共享堆内存,读写变量;
  2. 条件变量 / 信号量:协调线程运行顺序、同步访问资源;

Node 中进程通信

Node.js 本身是单线程运行 JS 的(即主线程)。通过 libuv 实现了多线程的事件驱动、I/O 操作。

  • 通过模块如 child_process 和 cluster 支持多进程并发;
  • 不同进程拥有独立的 V8 实例和内存空间,无法直接共享内存;
  • 所以通信必须通过操作系统提供的 IPC(Inter-Process Communication)机制;

(1)进程通信(主进程 ↔ 子进程)

Node 使用操心系统提供的 管道 pipe / socket 机制实现进程间通信(IPC)。

基本原理:

  1. Node 启动一个子进程(fork/spawn);
  2. 主进程与子进程之间会由操作系统自动建立一对 全双工管道(pipe);
  3. Node 对这对 pipe 封装为 JS 层的 IPC 通道;
  4. 通信通过 .send() / process.on('message') 完成,内部使用 JSON.stringify 编码消息;

(2)线程通信(Worker Threads)

  • worker_threads 模块
  • 使用 postMessageon('message') 通信(借鉴浏览器 Web Worker)

Node 架构服务端优/劣势

Nodejs 的主要架构特性:

特性描述
单线程 + 异步非阻塞JS 执行在单线程中,通过事件循环处理 I/O,高效应对并发连接
V8 引擎 + libuvV8 提供极快 JS 执行速度,libuv 实现跨平台异步 I/O
模块化、轻量化按需引入模块,服务启动速度快,部署简单
前后端同构JS 可统一前后端技术栈,提升协作效率
丰富的 NPM 生态世界上最大的包管理系统,快速构建业务

(1)相较于传统服务端语言的优势

  1. 高并发 I/O 处理能力(事件驱动 + 非阻塞 I/O)
    • Node 使用事件循环(event loop)而不是多线程并发处理请求;
    • 非阻塞 I/O 非常适合 高并发连接 + I/O 密集 场景,如:网关层、API 接口、实时服务(WebSocket)
    • 比如:Java 需要每个请求分配线程(线程上下文切换代价高);Node 单线程处理所有请求(避免切换,低开销)。
  2. 启动快、资源占用少、部署轻便
    • Node.js 启动极快(V8 编译执行,非 JVM),单文件可运行,部署简单,容器化友好。
    • 适合:简单的微服务、Serverless 场景、中间层/网关(如 BFF);
  3. 前后端技术统一、开发效率高
    • 同一团队使用 JavaScript 开发前端和后端
    • 可复用工具链、代码逻辑(如数据校验、模型结构
  4. 生态与社区支持(NPM)
    • 可以用已有模块构建复杂服务(Express/Koa 简单场景、NestJS 复杂场景)。

(2)劣势

  1. 单线程限制:CPU 密集任务性能差。I/O 处理优秀,CPU 密集处理差。
    • Node 是单线程的,如果执行:加密计算、图像处理、视频编解码、大量同步计算逻辑,会阻塞整个事件循环,导致服务卡顿或延迟。
    • 使用 child_process 多进程、worker_threads 多线程处理;
  2. 异步编程复杂,容易回调地狱(Callback Hell)。基于回调机制处理异步问题,虽然有 asymc/await 但仍有回调地狱的可能性,错误追踪不清晰,不容易维护。
  3. 类型安全较弱。相比于 Java/Go,Js 动态类型语言约束力差,需要搭配 Ts 解决。
  4. 多线程/多进程调度复杂度高。使用 child_process 多进程、worker_threads 多线程处理。但依然没有具备 Java 类似 dubbo 的 RPC 接口的多编程范式成熟。

Node 中间件

中间件概念:是 Node 中用于统一处理请求流程、复用通用逻辑、增强扩展性设计模式。

中间件机制:将 Web 请求处理流程拆解成可插拔、复用、组合的子模块,从而增强代码的灵活性;

具体来说,中间件(Middleware)是在请求(Request)到达路由处理器之前 / 响应(Response)发回客户端之前,用于处理、修改、拦截请求或响应的一类函数。

常用于:

  1. 逻辑复用:避免重复代码。
    • 类似 React 高阶组件的概念,不需要每个路由都写一遍验证,解析等逻辑,提高复用性。
  2. 请求前置处理:统一拦截、验证、改写请求:
    • 权限校验(判断用户登录、操作权限);
    • 参数解析(校验参数,类型转换);
    • 日志打印(记录请求方法、URL、耗时);
    • 请求限流(判断请求频率是否过高);
  3. 响应后处理:统一输出格式、处理错误
    • 修改响应结构,统一进行格式化输出:msg + data + code;
    • 统一处理错误捕获与异常信息返回;
    • 设置响应头,如 CORS、Content-Type;

Nginx 和 Node 中间件

Nginx 是高性能、系统级的“纯转发代理”工具,适合生产环境;而 Node 中间件代理是开发灵活、可插拔的“逻辑代理”,适合开发阶段或特定业务流控制。

特性Nginx 代理Node 中间件代理(如 http-proxy-middleware)
📍 所处层级系统层 / Web Server 层应用层 / JS 运行时层
🧠 工作原理通过配置文件,在内核/系统层转发 HTTP 请求在 Node 应用内部捕获请求,用 JS 发起代理请求
🧰 使用方式配置 location + proxy_pass写中间件代码并插入到 Express/Koa 的中间件链
⚡ 性能高:C 语言实现,运行于系统层,无需 JS 执行低:JS 执行,受事件循环、GC、请求堆积等影响
📦 可用场景静态资源服务、反向代理、负载均衡、SSL、压缩前端开发代理、BFF 请求合并、权限校验后的转发
🔄 是否能修改请求体❌ 默认不处理内容体,偏向透明转发✅ 可以任意修改请求内容/响应内容
🧱 稳定性/可靠性高:可独立部署、支持守护/高可用中:依赖 Node 应用,易受内存/错误/性能影响

相互的关系:[客户端请求] → [Nginx] → [Node 应用(含中间件代理)] → [微服务/API/DB]

  1. Nginx 更适合场景:

    • Nginx api 路由,拦截请求并转发到指定后端服务器,负载均衡;

    • 服务健康检查,统一 HTTPS/SSL 入口,统一做缓存和转发。

  2. Node 中间件场景:具体到某一业务,处理请求上下文、参数校验、权限逻辑等。

Nginx 的作用

Nginx(“Engine-X”)是一个轻量的 Web 服务器、反向代理服务器,支持、高性能、高并发,使用事件驱动,能处理百万级连接。

特性描述
🔁 反向代理接收客户端请求,转发给后端服务(如 Node、Java、Python)
📦 静态资源服务器直接服务 HTML、CSS、JS、图片等文件,性能极高
⚖️ 负载均衡多后端服务分担压力(轮询、最少连接、IP hash 等策略)
🔐 HTTPS 网关(TLS 终端)管理证书、终结 TLS 连接,实现 SSL 卸载
🌐 作为 API 网关路由、限流、CORS、URL 重写等
📈 缓存 & 压缩支持减少后端压力,启用 gzip、浏览器缓存
🚪 安全防护限制请求速率、过滤 IP、配置跨域等

具体来说:

  1. 反向代理 + API 网关:用于把前端请求转发到后端 Node、Java、Python 服务。前端请求统一走 Nginx,Nginx 决定发往哪个微服务。
    • 可以进行 URL 路由重写、添加请求头等;
  2. 静态资源服务器:直接返回 HTML、JS、CSS、图片等资源,不占用服务端性能资源;
  3. 负载均衡:轮训、最少连接等多种策略,合理对后端服务进行分流和转发;
  4. SSL 证书验证:Nginx 对 SSL 证书统一处理,后端服务只需处理 HTTP,简化部署。