vue源码理解之Vue批量异步更新和虚拟DOM和Diff算法

news2024/9/24 9:18:03

一:异步更新队列
1、Vue高效的秘诀是一套批量、异步的更新策略
在这里插入图片描述
概念:
在这里插入图片描述

  • 事件循环
    事件循环:浏览器为了协调事件处理、脚本执行、网络请求和渲染等任务而制定的一套工作机制。

  • 宏任务
    代表一个个离散的、独立工作单元。浏览器完成一个宏任务,在下一个宏任务执行开始前,会对页面进行重新渲染。主要包括创建主文档对象、解析HTML、执行主线JS代码以及各种事件如页面加载、输入、网络事件和定时器等。

  • 微任务
    微任务是更小的任务,是在当前宏任务执行结束后立即执行的任务。如果存在微任务,浏
    览器会清空微任务之后再重新渲染。微任务的例子有 promise 回调函数、DOM发生变化等

2、vue中的具体实现

异步:只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变
更。
批量:如果同一个 watcher 被多次触发,只会被推入到队列中一次。去重对于避免不必要的计算
和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列执行实际工作。
异步策略:Vue 在内部对异步队列尝试使用原生的 Promise.then 、 MutationObserver 和
setImmediate ,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替

update() core\observer\watcher.js
dep.notify()之后watcher执行更新,执行入队操作
queueWatcher(watcher) core\observer\scheduler.js
执行watcher入队操作
nextTick(flushSchedulerQueue) core\util\next-tick.js
nextTick按照特定异步策略执行队列操作

二:虚拟DOM
1、概念
虚拟DOM(Virtual DOM)是对DOM的JS抽象表示,它们是JS对象,能够描述DOM结构和关系。应用的各种状态变化会作用于虚拟DOM,最终映射到DOM上
在这里插入图片描述
体验虚拟DOM

<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
 <div id="app"></div>
 <!--安装并引入snabbdom-->
 <script src="node_modules/snabbdom/dist/snabbdom.js"></script>
 <script>
  const obj = {}
// 获取patch函数
  const { init, h } = snabbdom;
  const patch = init([])
  // 保存旧的vnode
  let vnode;
  function defineReactive(obj, key, val) {}
  // 更新
  function update() {
   // 修改为patch方式做更新,避免了直接接触dom
   vnode = patch(vnode, h('div#app', obj.foo))
 }
  defineReactive(obj, 'foo', new Date().toLocaleTimeString())
  // 初始化
  vnode = patch(app, h('div#app', obj.foo))
  console.log(vnode); 
  setInterval(() => {
   obj.foo = new Date().toLocaleTimeString()
 }, 1000);
 </script>
</body>
</html>

2、优点
虚拟DOM轻量、快速:当它们发生变化时通过新旧虚拟DOM比对可以得到最小DOM操作量,从
而提升性能

patch(vnode, h('div#app', obj.foo))

跨平台:将虚拟dom更新转换为不同运行时特殊操作实现跨平台

const patch = init([snabbdom_style.default])
patch(vnode, h('div#app', {style:{color:'red'}}, obj.foo))

兼容性:还可以加入兼容性代码增强操作的兼容性

3、必要性
vue 1.0中有细粒度的数据变化侦测,它是不需要虚拟DOM的,但是细粒度造成了大量开销,这对于大型项目来说是不可接受的。因此,vue 2.0选择了中等粒度的解决方案,每一个组件一个watcher实例,这样状态变化时只能通知到组件,再通过引入虚拟DOM去进行比对和渲染

4、整体流程
mountComponent() core/instance/lifecycle.js
渲染、更新组件

// 定义更新函数
const updateComponent = () => {
  // 实际调用是在lifeCycleMixin中定义的_update和renderMixin中定义的_render
  vm._update(vm._render(), hydrating)
}
_render core/instance/render.js
生成虚拟dom
_update core\instance\lifecycle.js
update负责更新dom,转换vnode为dom
__patch__() platforms/web/runtime/index.js
__patch__是在平台特有代码中指定的
Vue.prototype.__patch__ = inBrowser ? patch : noop

5、patch获取
patch是createPatchFunction的返回值,传递nodeOps和modules是web平台特别实现

export const patch: Function = createPatchFunction({ nodeOps, modules })
platforms\web\runtime\node-ops.js
定义各种原生dom基础操作方法
platforms\web\runtime\modules\index.js
modules 定义了属性更新实现
watcher.run() => componentUpdate() => render() => update() => patch()

6、patch实现
patch core\vdom\patch.js

首先进行树级别比较,可能有三种情况:增删改。
new VNode不存在就删;
old VNode不存在就增;
都存在就执行diff执行更新
在这里插入图片描述
patchVnode
比较两个VNode,包括三种类型操作:属性更新、文本更新、子节点更新
具体规则如下

  1. 新老节点均有children子节点,则对子节点进行diff操作,调用updateChildren
  2. 如果老节点没有子节点而新节点有子节点,先清空老节点的文本内容,然后为其新增子节点。
  3. 当新节点没有子节点而老节点有子节点的时候,则移除该节点的所有子节点。
  4. 当新老节点都无子节点的时候,只是文本的替换

updateChildren
updateChildren主要作用是用一种较高效的方式比对新旧两个VNode的children得出最小操作补丁。执
行一个双循环是传统方式,vue中针对web场景特点做了特别的算法优化
在这里插入图片描述
在新老两组VNode节点的左右头尾两侧都有一个变量标记,在遍历过程中这几个变量都会向中间靠拢。
当oldStartIdx > oldEndIdx或者newStartIdx > newEndIdx时结束循环
下面是遍历规则
首先,oldStartVnode、oldEndVnode与newStartVnode、newEndVnode两两交叉比较,共有4种比较
方法。
当 oldStartVnode和newStartVnode 或者 oldEndVnode和newEndVnode 满足sameVnode,直接将该
VNode节点进行patchVnode即可,不需再遍历就完成了一次循环。如下图
在这里插入图片描述
如果oldStartVnode与newEndVnode满足sameVnode。说明oldStartVnode已经跑到了oldEndVnode
后面去了,进行patchVnode的同时还需要将真实DOM节点移动到oldEndVnode的后面
在这里插入图片描述
如果oldEndVnode与newStartVnode满足sameVnode,说明oldEndVnode跑到了oldStartVnode的前
面,进行patchVnode的同时要将oldEndVnode对应DOM移动到oldStartVnode对应DOM的前面
在这里插入图片描述
如果以上情况均不符合,则在old VNode中找与newStartVnode满足sameVnode的vnodeToMove,若
存在执行patchVnode,同时将vnodeToMove对应DOM移动到oldStartVnode对应的DOM的前面
在这里插入图片描述
当然也有可能newStartVnode在old VNode节点中找不到一致的key,或者是即便key相同却不是
sameVnode,这个时候会调用createElm创建一个新的DOM节点
在这里插入图片描述
至此循环结束,但是我们还需要处理剩下的节点。
当结束时oldStartIdx > oldEndIdx,这个时候旧的VNode节点已经遍历完了,但是新的节点还没有。说
明了新的VNode节点实际上比老的VNode节点多,需要将剩下的VNode对应的DOM插入到真实DOM
中,此时调用addVnodes(批量调用createElm接口)
在这里插入图片描述
但是,当结束时newStartIdx > newEndIdx时,说明新的VNode节点已经遍历完了,但是老的节点还有
剩余,需要从文档中删 的节点删除
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/672748.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Visual Studio】Qt 的实时绘图曲线功能,使用 C++ 语言,配合 Qt 开发串口通信界面

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 战斗背景&#xff1a;做了个串口接收界面&#xff0c;用来接收传输过来的信号。但是光用数字显示太单调&#xff0c;需要用图线显示出来。 战略目标&#x…

Java 从入门到精通(续集6)——集合框架

Java 从入门到精通&#xff08;续集6&#xff09;——集合框架 在 Java 中&#xff0c;集合是一种用于存储对象的容器&#xff0c;可以方便地进行增删改查等操作。Java 提供了一套完整的集合框架&#xff0c;并且在 Java 5 中引入了泛型&#xff0c;使得集合变得更加灵活。 一、…

Redis哨兵部署

Redis哨兵 单机安装部署 yum install epel-release -yYum install redis -yMkdir /root/redisCd /root/rediscp /etc/redis.conf .cp redis.conf redis_6379.confcp redis.conf redis_6380.confcp redis.conf redis_6381.conf vim redis_6379.conf 1.配置redis.conf文件中的…

极致呈现系列之:Echarts水球图的灵动魅力

目录 水球图简介什么是水球图水球图的特点和用途水球图的安装和引入水球图的常用配置项创建基本的水球图自定义水球图样式水球图中的shape属性使用SVG代码自定义水球图水球图简介 什么是水球图 水球图是一种通过一个圆形的容器来展示数据的图表类型。它以水球作为图形的基本元…

【C++篇】封装类和对象

友情链接&#xff1a;C/C系列系统学习目录 知识总结顺序参考C Primer Plus&#xff08;第六版&#xff09;和谭浩强老师的C程序设计&#xff08;第五版&#xff09;等&#xff0c;内容以书中为标准&#xff0c;同时参考其它各类书籍以及优质文章&#xff0c;以至减少知识点上的…

【机器学习】十大算法之一 “逻辑回归”

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,活动,python领域博主爱笑的男孩。擅长深度学习,活动,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typeblog个…

MindSpore-FCOS模型权重迁移推理对齐实录

准备工作 环境&#xff1a; wsl2 Ubuntu 20.04 mindspore 2.0.0 python 3.8 pytorch 2.0.1 cpu 基于已有的mindspore FCOS项目和FCOS官方pytorch权重来做迁移&#xff0c; FCOS官方pytorch实现 FCOS_imprv_R_50_FPN_1x权重 MindSpore FCOS项目链接 该代码是mindspore1.6实现…

【加强版】SAX解析XML返回对应格式的Map对象(解决元素递归嵌套)

SAX解析XML返回对应格式的Map对象_辛丑年正月十五的博客-CSDN博客 前言 上篇文章实现了xml元素节点的解析并返回了对应格式的Map对象&#xff0c;但是遗留了一个问题&#xff0c;就是当xml中的元素存在递归嵌套时就解析不了&#xff0c;因为qname属性会重复&#xff0c;导致后…

DDD软件架构领域驱动设计

目录 1. DDD概述1.1 软件开发的困境1.2 DDD的来源及简介1.2.1 DDD设计方法 1.3 DDD解决了什么问题1.3.1 沟通问题1.3.2 代码质量问题 1.4 模型和建模1.4.1 什么是模型 1.5 统一语言&#xff08;UBIQUITOUS LANGUAGE&#xff09;1.6 什么是DDD 2. 传统开发模式2.1 基础知识回顾2…

Debian12.0.0更换系统语言中文到英文

6月10号&#xff0c;Debian12.0.0更新&#xff0c;想尝尝鲜&#xff0c;在虚拟机里安装好&#xff0c;想将中文改为英文&#xff0c;因为Terminal下输入命令&#xff0c;中文切换麻烦。 一、步骤如下 #1、查看当前语言环境 env | grep LANG #2、en表示语言&#xff0c;US表示…

欧科云链在GEF论坛发起圆桌:监管科技与Web3合规发展图景与展望

6月15日&#xff0c;欧科云链在格林威治经济论坛发起了一场题为“监管科技与Web3合规发展图景与展望”的圆桌会议&#xff0c;此次会议由中国香港贸易发展局副执行董事PatrickLau博士主持。Stratford Finance首席执行官Angelina Kwan&#xff0c;BC科技集团有限公司董事会副主席…

[Web前端] Servlet及应用

文章目录 前言1、简介1.1、Servlet 架构1.1.1、Servlet 任务1.1.2、Servlet 包 1.2、Servlet 环境设置1.2.1、设置 Web 应用服务器&#xff1a;Tomcat 1.3、Servlet 生命周期1.3.1、init() 方法1.3.2、service() 方法1.3.3、doGet() 方法1.3.4、doPost() 方法1.3.5、destroy() …

采集发布到WordPress 特色图片(缩略图)无法显示

采集的数据发布到wordpress系统网站&#xff0c;文章内容是正常的&#xff0c;但是在列表页的缩略图&#xff08;特色图片&#xff09;却是显示失败。 这种情况有多种问题都可以造成的&#xff0c;可按照以下步骤逐一排查&#xff1a; 目录 1. 发布映射值是否正确 2. 与主题…

【Python 基础篇】Python 字符串以及字符串常用函数

文章目录 导言一、字符串基础二、字符串操作1、字符串拼接2、字符串格式化3、字符串常用函数len()lower()upper()strip()split()join()replace()find()count() 三、条件控制与字符串总结 导言 字符串是计算机编程中常用的数据类型之一。在 Python 中&#xff0c;字符串是由字符…

切换SVN登录的账号

更换SVN的账号 1、找到已登录的用户信息2、删除已登录的用户信息3、获取重输用户信息弹窗4、使用新的用户信息登录 1、找到已登录的用户信息 &#xff08;1&#xff09;在任何文件夹里面右键&#xff0c;找到TortoiseSVN&#xff0c;然后选择里面的Settings &#xff08;2&am…

【Python 基础篇】Python 条件与循环控制

文章目录 导言一、条件语句1、if-elif-else 结构2、嵌套条件语句3、单行 if 语句 二、循环语句1、while 循环while 循环的高级用法 2、for 循环for 循环的高级用法 示例一&#xff1a;输出 1 到 10 的偶数示例二&#xff1a;获取 100 以内的质数结论 导言 Python 是一种简单而…

【Leetcode -2236.判断根节点是否等于子节点之和 -2331.计算布尔二叉树的值】

Leetcode Leetcode -2236.判断根节点是否等于子节点之和Leetcode -2331.计算布尔二叉树的值 Leetcode -2236.判断根节点是否等于子节点之和 题目&#xff1a;给你一个 二叉树 的根结点 root&#xff0c;该二叉树由恰好 3 个结点组成&#xff1a;根结点、左子结点和右子结点。 …

shell脚本自动化部署tomcat

前言 在一个月黑风高的晚上&#xff0c;在公司把程序打包好后&#xff0c;发给现场&#xff0c;结果又被告知不能登录命令行界面部署程序&#xff08;tomcat部署&#xff09;&#xff0c;只能提供一个shell脚本实现自动化部署&#xff0c;于是拿出我0.5年的开发经验&#xff0…

Spring MVC获取参数和自定义参数类型转换器及编码过滤器

目录 一、使用Servlet原生对象获取参数 1.1 控制器方法 1.2 测试结果 二、自定义参数类型转换器 2.1 编写类型转换器类 2.2 注册类型转换器对象 2.3 测试结果 三、编码过滤器 3.1 JSP表单 3.2 控制器方法 3.3 配置过滤器 3.4 测试结果 往期专栏&文章相关导读…

MySQL数据库学习笔记二

数据库存储引擎 数据库存储引擎是数据库底层软件组织&#xff0c;数据库管理系统&#xff08;DBMS&#xff09;通过数据引擎&#xff0c;对数据进行创建、查询、修改和删除的操作。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能&#xff0c;使用不同的存储引擎…