浅谈 React 与 Vue 更新机制的差异
前言
哈喽,大家好,我是 Baker !🎉
对于前端的 Vue 和 React 相信大家并不陌生,这两个库有着截然不同的设计思想和发展目标,对于我们上层使用者来说,研究它们的差异不仅让我们更加深入的去理解这些库的设计思想,也能帮助我们在开发中更有依据的去选择合适的框架。
本篇文章就两者的更新机制来浅淡一下它们的区别,因为是浅淡并且也受限于篇幅,所以这里并不会就某些技术细节进行展开,如果大家想要深入去了解两者技术的实现,可以尝试去读一下源码,或者期待一波博主后续的更新🌹
因为不涉及技术细节,大家放轻松,悠闲自在的观看即可。

好啦,让我们开始吧!
Vue 更新机制
Vue 使用著名的响应式系统来收集依赖和派发更新,当模板中数据发生变化时,组件的 render 函数会被作为数据的依赖而被触发,只不过这个触发并不是立刻的,因为模板中会引用很多数据,render 同时是这些所有数据的依赖项,如果 render 每次都立刻执行,则会造成多次重复渲染而消耗性能。
实际上 render 是被 update 调用的,而 update 又是 Watcher 调用的,而 Watcher 在收到 Dep 的派发更新时会把自身交给 Scheduler ,由 Scheduler 负责对其去重并通过 nextTick() 将这些 Watcher 包装成微任务放入到事件循环中等待调用。
render 执行输出的结果是一颗新的虚拟 DOM 树,然后 update 会通过 patch 函数将它与旧的虚拟 DOM 树进行对比,diff 和真实 DOM 的操作过程既是在 patch 函数中进行。
所以,其实 Vue 的整个更新任务(构建虚拟 DOM ,diff,操作真实 DOM)可以算作一个整体,这个整体被当作微任务来处理,这也就是 Vue 异步更新的原理。
最后附上一张 Vue 官方文档的流程图:

React 更新机制
这里以 React16 之后的 Fiber 架构为例。
React 没有 Vue 的响应式系统,它的更新主要是靠用户手动的调用副作用函数(比如 setState 等)来触发( Vue 则是系统自动触发)。所有的更新任务(如调用 setState )会被 render 阶段的 Scheduer 进行调度,它会将多次的 setState 调用合并为一次更新操作,render 阶段可以随时被打断(如遇到高优先级任务、当前事件循环没有足够的时间了、发生了其它错误等)。
Scheduer 调度器负责对任务进行调度,内部会通过任务的优先级(会通过一系列的优先级设置任务的 delay ),以及当前事件循环空闲的时间等来判断当前任务是否可以执行,如果可以执行则会通过 MessageChannel 将任务包装成一个宏任务推入到事件循环当中等待执行,在任务执行时通过 render 阶段的 Reconciler 来进行协调。
Scheduer 判断当前任务是否可以执行的调度可以通过原生的 requestIdleCallback 来进行简单模拟,因为这个 Api 就是在浏览器空闲时期被调用。
Reconciler 通过递和归两个阶段来创建新的 Fiber Tree 和进行副作用的收集(这个副作用指的是 DOM 的更新操作)( diff 发生在此),至此 render 阶段结束,之后就来到了 commit 阶段,该阶段只有一个 Readerer 执行,并且是同步执行,Readerer 主要就是根据 Fiber Tree 标记的副作用来进行真实 DOM 的创建、更新和删除操作。

区别
其实大体上 Vue 和 React 的更新机制都是构建虚拟DOM、diff、操作真实DOM这三个主要过程,它们的主要区别主要体现在触发更新的机制和一些细节上:
-
触发更新机制:Vue 是通过响应式系统自动及时的进行触发,而 React 则是通过用户更改状态的操作然后进行一系列调度来触发更新。
-
任务的区别:Vue 会将任务包装成微任务,而 React 则是将其包装成宏任务。
虽然都是异步任务,但它们有很大区别。在事件循环中,如果有微任务存在则会先一直执行微任务,直到把微任务队列清空,然后再执行宏任务,并且在每个宏任务执行完毕后,会立即检查并执行所有微任务,然后再进行下一个宏任务的执行。
先明确一点:异步任务执行时是由主线程进行执行的,所以此时它们已经相当于是同步执行了(这个异步实际指的是异步任务在任务队列里面等待的时候不会影响主线程的执行)
微任务执行时不会穿插其它任务(比如浏览器渲染),所以当有大量微任务堆积时可能就会阻塞浏览器渲染(异步任务),但执行完一个宏任务时如果遇到浏览器需要渲染,则不会继续执行下一个宏任务而是转去进行浏览器渲染然后开启新的一轮事件循环。
- 因为 React 的 Fiber 架构的出现就是为了能够随时打断,把控制权交给主线程,所以 React 采用的是宏任务,而不是会一股脑 “ 全冲完 ” 的微任务,这样可以避免微任务过多而导致的任务堆积和性能问题。
- 也正是因为 Vue 的理念是追求响应性和即时效果并避免过多的渲染,所以它采用微任务,及时把更新任务处理完,最后让浏览器渲染一次即可。
- 其实现如今,React 和 Vue 都不是完全使用某一种任务,在一些情况下 React 也会使用微任务,Vue 亦是如此,它们的目标都是想要结合自身情况来创造一个更优秀的框架。
-
diff 算法的不同:Vue 采用双端对比,而 React 使用的是 Reconciliation 算法。
Reconciliation 算法是React 整体的更新策略,并不只是简单的 diff 算法。简单来说 React 的 diff 过程会对单节点和多节点分别计算(都是遍历 Fiber 链表进行的),多节点的情况会分两轮遍历,第一轮遍历会尝试逐个的复用节点,第二轮遍历处理上一轮遍历中没有处理完的节点。
React 不使用双端 diff 的原因:

React 源码中的原话:This algorithm can’t optimize by searching from boths ends since we don’t have backpointers on fibers. I’m trying to see how far we can get with that model. If it ends up not being worth the tradeoffs, we can add it later.(这种算法不能通过从两端搜索来优化,因为我们在 Fiber 对象上没有反向指针。我正试着看看我们能用这个模型走多远,如果这种方式不理想,以后再考虑实现两端搜索。)
并且源码中还提到,React 认为对于列表反转和需要进行双端搜索的场景是少见的,所以综合以上情况, React 暂时还不会使用双端 diff。
结语
还是那句话,本篇文章只是浅淡,Vue 和 React 的更新机制非常复杂,其中涉及的知识点很多,比如 Vue 的响应式原理、Vue 双端 diff 流程、React Fiber 架构、Fiber 双缓冲、React Reconciler 递和归两阶段流程等等。
本文简单梳理了一下它们更新机制的整体流程,希望能基于此来为大家展开一个清晰的脉络,如果在阅读过程中发现了问题,欢迎评论区留言交流!
如果本篇文章对你起到了帮助,还请大家不要吝啬手中的点赞、评论、收藏和关注,我们下次再见!
最近忙着秋招,更新频率虽然降低了,但是我写文章的热情可是一点没减,期待下次好文相见!
相关文章:
浅谈 React 与 Vue 更新机制的差异
前言 哈喽,大家好,我是 Baker !🎉 对于前端的 Vue 和 React 相信大家并不陌生,这两个库有着截然不同的设计思想和发展目标,对于我们上层使用者来说,研究它们的差异不仅让我们更加深入的去理解…...
Delft3D水动力与泥沙运动模拟实践技术应用
水体中泥沙运动是关系到防洪,调水等方面的重要问题,也是水利和水环境领域科研热点之一。水利数值模型,在环境影响评价、防洪规划等方面也有着广泛的应用。荷兰Delft研究所开发的Delft3D模型是世界上最先进的水动力之一,能够运用于…...
Linux 本地Yearning SQL 审核平台远程访问
文章目录 前言1. Linux 部署Yearning2. 本地访问Yearning3. Linux 安装cpolar4. 配置Yearning公网访问地址5. 公网远程访问Yearning管理界面6. 固定Yearning公网地址 前言 Yearning 简单, 高效的MYSQL 审计平台 一款MYSQL SQL语句/查询审计工具,为DBA与开发人员使用…...
Redis集群(Cluster)
1. 什么是集群 广义的集群:只要是多台机器,构成一个分布式系统,就可以称为一个“集群”。像前面的主从结构,哨兵模式都是“广义的集群”狭义的集群:redis提供的集群模式,这个集群模式主要解决存储空间不足…...
Scapy 解析 pcap 文件从HTTP流量中提取图片
Scapy 解析 pcap 文件从HTTP流量中提取图片 前言一、网络环境示例二、嗅探流量示例三、pcap 文件处理最后参考 作者:高玉涵 时间:2023.9.17 10:25 环境:Linux kali 5.15.0-kali3-amd64,Python 3.11.4,scapy…...
难得有个冷静的程序员发言了:纯编码开发实施的项目,失败的案例也有很多
难得有个冷静的程序员发言了:纯编码开发实施的项目,失败的案例也有很多。假如用低代码实施,能达到不失败或提高成功率,对软件开发项目交付,会是重大的价值。 我的观点:两者都可能失败,不同的是&…...
Leetcode.146 LRU 缓存
题目链接 Leetcode.146 LRU 缓存 mid 题目描述 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 c a p a c i t y capacity capacity 初始化 LRU 缓存int get(int key) 如果关键…...
科技资讯|Canalys发布全球可穿戴腕带设备报告,智能可穿戴增长将持续
市场调查机构 Canalys 近日发布报告,表示 2023 年第 2 季度全球可穿戴腕带设备出货量达 4400 万台,同比增长了 6%。 主要归功于其亲民的价格以及消费者对价位较高的替代品仍持谨慎态度,基础手环市场尽管与去年同期相比有所下降,…...
使用https接口,无法调通接口响应不安全
网页pc使用不安全https 页面提示不安全–点击高级–跳过 接口使用部安全https 无法像页面一样可以跳过 方法:安装证书 还是无法响应报错不安全: 1、确定证书绑定ip还是域名(ip和域名都可以绑定) 使用的是httpsip,报…...
uniapp开发h5,解决项目启动时,Network: unavailable问题
网上搜了很多,发现都说是要禁用掉电脑多余的网卡,这方法我试了没有好,不晓得为啥子,之后在网上看,uniapp的devServer vue2的话对标的就是webpack4的devserver(除了复杂的函数配置项),…...
9.17 校招 实习 内推 面经
绿泡*泡: neituijunsir 交流裙 ,内推/实习/校招汇总表格 1、自动驾驶一周资讯 - 一汽与Mobileye 签署战略合作,小鹏汽车将用经销商销售逐渐替换直营模式,原小鹏汽车副总裁加盟赛力斯 自动驾驶一周资讯 - 一汽与Mobileye 签署战…...
【Python小项目之Tkinter应用】随机点名/抽奖工具大优化:新增查看历史记录窗口!语音播报功能!修复预览文件按钮等之前版本的bug!
文章目录 前言一、实现思路二、关键代码查看历史记录按钮语音播报按钮三、完整代码总结前言 老生常谈,先看效果:(订阅专栏可获取完整代码) 初始状态下,我们为除了【设置】外的按钮添加弹窗,提示用户在使用工具之前要先【设置】。在设置界面,我们主要修改了【预览文件】…...
数据结构与算法:排序算法(1)
目录 冒泡排序 思想 代码实现 优化 鸡尾酒排序 优缺点 适用场景 快速排序 介绍 流程 基准元素选择 元素交换 1.双边循环法 使用流程 代码实现 2.单边循环法 使用流程 代码实现 3.非递归实现 排序在生活中无处不在,看似简单,背后却隐藏…...
NotePad++ 在行前/行后添加特殊字符内容方法
我们在处理数据时,会遇到需要在每行数据前面、后面、开头、结尾添加各种不一样的字符 如果数据不多,我们可以自己手动的去添加,但如果达到了成百上千行,此时再机械的手动添加是不现实的 这里教给大家如何快速的在数据每行的前后…...
【JavaEE】多线程案例-线程池
文章目录 1. 什么是线程池2. 为什么要使用线程池(线程池有什么优点)3. 如何使用Java标准库提供的线程池3.1 创建一个线程池对象3.2 什么是工厂模式3.3 为什么要使用工厂模式3.4 Executors 创建不同具有不同特性的线程池3.5 ThreadPool 类的构造方法3.6 线…...
服务器搭建(TCP套接字)-fork版(服务端)
基础版的服务端虽然基本实现了服务器的基本功能,但是如果客户端的并发量比较大的话,服务端的压力和性能就会大打折扣,为了提升服务端的并发性能,可以通过fork子进程的方式,为每一个连接成功的客户端fork一个子进程,这样…...
缺失的第一个正数:高效解法与技术
缺失的第一个正数:高效解法与技术 背景 在计算机编程中,有时候需要寻找一个未排序整数数组中没有出现的最小的正整数。这篇技术博客将详细讨论这个问题,并提供一个时间复杂度为 O(n) 且只使用常数级别额外空间的解决方案。 问题描述 leet…...
常用的辅助网站(持续更新)
标题 一、uni-app方向二、H5方向 一、uni-app方向 1、uni-app官网 地址:https://uniapp.dcloud.net.cn/ 2、香蕉云编 地址:https://www.yunedit.com/ 描述:一般用来配置ios证书或安卓证书、上传ios包至商店 3、uView 地址:http…...
LeetCode 75 - 01 : 最小面积矩形
type pair struct{x, y int }func minAreaRect(points [][]int)int{mp : map[pair]struct{}{}// 将二维数组中的坐标映射到map中for i : range points{mp[pair{points[i][0], points[i][1]}] struct{}{}}// 将结果设置为一个最大值,防止影响求最小值的逻辑res : ma…...
每日一题:请解释什么是闭包(Closure)?并举一个实际的例子来说明。(前端初级)
今天继续在前端初级笔试题中被AI虐: 碱面的答案,问题:初级,回答:初级https://bs.rongapi.cn/1702510598371151872/14我的回答如下: 闭包是指由大括号包裹的一个区域,这个区域代表了一个变量生效…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
