Vue 双向绑定、diff和nextTick原理

  1. 前言

    1. 什么是虚拟dom

      1. virtual DOM 虚拟DOM,用普通js对象来描述DOM结构,因为不是真实DOM,所以称之为虚拟DOM。
        虚拟 dom 是相对于浏览器所渲染出来的真实 dom而言的,在react,vue等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 dom 树的方式找到需要修改的 dom 然后修改样式行为或者结构,来达到更新 ui 的目的。
        这种方式相当消耗计算资源,因为每次查询 dom 几乎都需要遍历整颗 dom 树,如果建立一个与 dom 树对应的虚拟 dom 对象( js 对象),以对象嵌套的方式来表示 dom 树及其层级结构,那么每次 dom 的更改就变成了对 js 对象的属性的增删改查,这样一来查找 js 对象的属性变化要比查询 dom 树的性能开销小。
    2. 为什么需要虚拟dom

      1. 在vue中,渲染视图会调用render函数,这种渲染不仅发生在组件创建时,同时发生在视图依赖的数据更新时。如果在渲染时,直接使用真实DOM,由于真实DOM的创建、更新、插入等操作会带来大量的性能损耗,从而就会极大的降低渲染效率。因此,vue在渲染时,使用虚拟dom来替代真实dom,主要为解决渲染效率的问题。
    3. 虚拟dom是如何转化为真实dom

      1. 在一个组件实例首次被渲染时,它先生成虚拟dom树,然后根据虚拟dom树创建真实dom,并把真实dom挂载到页面中合适的位置,此时,每个虚拟dom便会对应一个真实的dom。
        如果一个组件受响应式数据变化的影响,需要重新渲染时,它仍然会重新调用render函数,创建出一个新的虚拟dom树,用新树和旧树对比,通过对比,vue会找到最小更新量,然后更新必要的虚拟dom节点,最后,这些更新过的虚拟节点,会去修改它们对应的真实dom这样一来,就保证了对真实dom达到最小的改动。
  2. 双向绑定原理

    1. 简介

      1. 响应式数据的最终目标,是当对象本身或者对象属性发生变化时,将会运行一些函数,最常见的就是render函数,在具体实现上,vue使用了几个核心部件
        1. Observer

          1. Observer要实现的目标非常简单,就是把一个普通的对象转换为响应式的对象。为了实现这一点,Observer把对象的每个属性通过`0bject.defineProperty转换为带有gtter和setter 的属性,这样一来,当访问或设置属性时,vue就有机会做一些别的事情。Observer是vue内部的构造器,我们可以通过Vue提供的静态方法vue.observable(object)间接的使用该功能在组件生命周期中,这件事发生在beforecreate之后,created之前。具体实现上,它会递归遍历对象的所有属性,以完成深度的属性转换。由于遍历时只能遍历到对象的当前属性,因此无法监测到将来动态增加或删除的属性,因此vue 提供了$set 和$delete两个实例方法,让开发者通过这两个实例方法对已有响应式对象添加或删除属性。对于数组,vue 会更改它的隐式原型,之所以这样做,是因为vue需要监听那些可能改变数组内容的方法。总之,Observer的目标,就是要让一个对象,它属性的读取、赋值,内部数组的变化都要能够被vue感知到。
        2. Dep

          1. 这里有两个问题没解决,就是读取属性时要做什么事,而属性变化时要做什么事,这个问题需要依靠Dep来解决。
            Dep的含义是Dependency,表示依赖的意思。vue 会为响应式对象中的每个属性、对象本身、数组本身创建一个Dep 实例,每个Dep 实例都有能力做以下两件事:
            记录依赖:是谁在用我
            派发更新:我变了,我要通知那些用到我的人
            当读取响应式对象的某个属性时,它会进行依赖收集:有人用到了我当改变某个属性时,它会派发更新:那些用我的人听好了,我变了
        3. Watcher

          1. 这里又出现一个问题,就是Dep如何知道是谁在用我?要解决这个问题,需要依靠另一个东西,就是Watcher。当某个函数执行的过程中,用到了响应式数据,响应式数据是无法知道是哪个函数在用自己的因此,vue通过一种巧妙的办法来解决这个问题我们不要直接执行函数,而是把函数交给一个叫做watcher的东西去执行,watcher是一个对象,每个这样的函数执行时都应该创建一个watcher,通过watcher去执行。
            watcher会设置一个全局变量,让全局变量记录当前负责执行的watcher等于自己,然后再去执行函数,在函数的执行过程中,如果发生了依赖记录dep.depend(),那么Dep就会把这个全局变量记录下来,表示:有一个watche用到了我这个属性。当Dep进行派发更新时,它会通知之前记录的所有watcher:我变了。
        4. Scheduler

          1. 现在还剩下最后一个问题,就是Dep通知watcher之后,如果watcher执行重运行对应的函数,就有可能导致函数频繁运行,从而导致效率低下试想,如果一个交给watcher的函数,它里面用到了属性a、b、c、d,那么a、b、c、d属性都会记录依赖,于是下面的代码将触发4次更新:
            1. state.a = "new data";
              state.b = "new data";
              state.c = "new data”;
              state.d = "new data";
              这样显然是不合适的,因此,watcher收到派发更新的通知后,实际上不是立即执行对应函数,而是把自己交给个叫调度器的东西。
              调度器维护一个执行队列,该队列同一个watcher仅会存在一次,队列中的watcher不是立即执行,它会通过一个叫做nextTick的工具方法,把这些需要执行的watcher放入到事件循环的微队列中,nextTick的具体做法是通过Promise 完成的。nextTick 通过 this.$nextTick 暴露给开发者
              nextTick 的具体处理方式见:https://cn,yuejs.org2/guide/reactivity,html#%E5%BC%82%E6%AD%A5%E6%9B%B4%E6%96%B0%E9%98%9F%E5%88%97
              也就是说,当响应式数据变化时,render函数的执行是异步的,并且在微队列中。
        5. 总体流程

          1. 首先原始对象交给Obsever,它会把它变成一个响应式对象,它具有getter和setter,假如有一个函数render要执行,他不是立即执行,而是把自己交给watcher,watcher会设置一个全局变量,然后运行这个函数,执行的过程中会用到响应式变量,然后属性就被收集过来了,这个过程叫做依赖收集,记录一个属性用到了这个watcher另外一个属性也用到了watcher,还有一些对象、数组用到watcher,这些都会记录,然后页面会渲染出来,当我们通过点击按钮等操作setter会做派发更新,之前有watcher用到了这个属性,然后通知watcher数据发生改变,之后watcher不是立即执行这个函数,不然的话会执行好多次,因为有可能很多数据都要不停的变化,它把自己交给一个调度器,这个调度器已经存在很多watcher,调度器的作用就是把这些watcher添加到队列中去,然后队列把执行自己的函数交给nextTick,nextTick中也会存在其它函数都是在微队列中,通过promise.then。因此等待同步代码完成以后,会异步的执行nextTick中的函数,当执行到watcher时又运行render函数,又用到响应式数据然后重新收集依赖,之前的依赖来会取消掉,循环往复。
  3. diff算法

    1. 参考回复

      1. 当组件创建和更新时,vue均会执行内部的update函数,该函数在内部调用render函数生成虚拟dom树,组件会指向新树,然后vue将新旧两树进行对比,找到差异点,最终更新到真实dom。
      2. 对比差异的过程叫diff,vue在内部通过一个叫patch的函数完成该过程。
      3. 在对比时,vue采用深度优先、逐层比较的方式进行比对。
      4. 在判断两个节点是否相同时,vue是通过虚拟节点的key和tag来进行判断。
      5. 具体来说,首先对根节点进行对比,如果相同则将旧节点关联的真实dom的引用挂到新节点上,然后根据需要更新属性到真实dom,然后再对比其子节点数组;如果不相同,则按照新节点的信息递归创建所有真实dom,同时挂到对应虚拟节点上,然后移除掉旧的dom。
      6. 在对比其子节点数组时,vue对每个子节点数组使用了两个指针,分别指向头尾,然后不断向中间靠拢来进对比,这样做的目的是尽量复用真实dom,尽量少的销毁和创建真实dom。如果发现相同,则进入和根节点一样的对比流程,如果发现不同,则移动真实dom到合适的位置。
      7. 这样一直递归的遍历下去,直到整棵树完成对比。
    2. diff的时机

    3. 当组件创建时,以及依赖的属性或数据变化时,会运行一个函数,该函数会做两件事:运行_render生成一棵新的虚拟dom树(vnode tree)。运行_update,传入虚拟dom树的根节点,对新旧两棵树进行对比,最终完成对真实dom的更新。
      核心代码如下:
      // vue构造函数
      function Vue()
      //..,其他代码
      var updateComponent=()=>{
      this._update(this._render()
      }
      new Watcher(updatecomponent);
      //..其他代码
      diff就发生在_update 函数的运行过程中
    4. _update 函数在干什么

      1. _update 函数接收到一个 vnode 参数,这就是新生成的虚拟dom树。
      2. 同时,_update函数通过当前组件的_vnode属性,拿到旧的虚拟dom树。
      3. _update函数首先会给组件的_vnode属性重新赋值,让它指向新树。
      4. 然后会判断旧树是否存在:
        1. 不存在:说明这是第一次加载组件,于是通过内部的patch函数,直接遍历新树,为每个节点生成真实DOM,挂载到每个节点的elm属性上。
        2. 存在:说明之前已经渲染过该组件,于是通过内部的patch函数,对新旧两棵树进行对比,以达到下面2个目标:
          1. 完成对所有真实dom的最小化处理
          2. 让新树的节点对应合适的真实dom
      5. patch函数的对比流程

        1. 「相同」:是指两个虚拟节点的标签类型、key值均相同,但input 元素还要看 type 属性。
        2. 「新建元素」:是指根据一个虚拟节点提供的信息,创建一个真实dom元素,同时挂载到虚拟节点的elm属性上。
        3. 「销毁元素」:是指:vnode.elm.remove()
        4. 「更新」:是指对两个虚拟节点进行对比更新,它仅发生在两个虚拟节点「相同」的情况下。具体过程稍后描述
        5. 「对比子节点」:是指对两个虚拟节点的子节点进行对比,具体过程稍后描述详细流程:
  4. nextTick

    1. 是什么

      1. nextTick是等待下一次 DOM 更新刷新的工具方法。
    2. 原理

      1. 在Vue内部,nextTick之所以能够让我们看到DOM更新后的结果,是因为我们传入的callback会被添加到队列刷新函数(flushSchedulerQueue)的后面,这样等队列内部的更新函数都执行完毕,所有dom操作也就结束了,callback自然能够获取到最新的DOM值。

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

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

相关文章

LabVIEW专栏八、类

该章目的是可以开发仪器类。 一、类的概述 一般来说类有三大特性,封装,继承和多态。 在实际项目中,最主要是继承和多态,要搞清楚这两者的概念和在LabVIEW中是怎样应用的。在LabVIEW中,面向对象编程用到的就是LabVIE…

SAM在低阶自适应航空土地覆盖分类中的应用2024.01

GEOSCIENCE AND REMOTE SENSING LETTERS 2024.01 提出了一种新的语义分割模型,该模型结合了SAM的图像编码器和低秩自适应方法(LoRA),用于航空图像的特征提取和微调。我们还使用了一个辅助CNN编码器来促进下游适应,并补充ViT编码器在密集视觉…

机器学习模型效果不好及其解决办法

当训练出来的机器学习模型效果不佳时,可能涉及多个方面的原因。为了改善模型的效果,需要系统地检查和分析问题的根源,并采取相应的措施进行优化。 一、数据问题 数据质量 检查数据是否干净、完整,是否存在噪声、异常值或缺失值。…

【MySQL】A01、性能优化-语句分析

1、数据库优化方向 A、SQL及索引优化 根据需求写出良好的SQL,并创建有效的索引,实现某一种需求可以多种写法,这时候我们就要选择一种效率最高的写法。这个时候就要了解sql优化 B、数据库表结构优化 根据数据库的范式,设计表结构&…

从C向C++14——STL初识及函数对象

一.STL初识 1.STL的诞生 长久以来,软件界一直希望建立一种可重复利用的东西C的面向对象和泛型编程思想,目的就是复用性的提升多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作为了建立数据结构和算法的一套标准,诞生了ST…

详解汽车充电桩主板的硬件设计与软件系统

随着电动汽车时代的到来,充电桩逐渐成为城市新地标。而在每一个充电桩的核心,隐藏着一颗强大的“心脏”——充电桩主板。 充电桩主板是充电桩的核心部件,决定着充电桩的充电效率、安全和用户体验。今天,我们将深入探索汽车充电桩主…

分布式版本控制工具 Git 的使用方式

文章目录 Git简介下载安装基本使用起始配置Git 的三个区域基本操作流程查看仓库状态删除(撤销暂存区)差异对比查看版本日志版本回退修改提交日志分支概念:创建分支与切换分支合并分支(快速合并)合并分支(提…

北京筑龙当选中招协第二届招标采购数字化专业委员会执行主任单位

4月18-19日,中国招标投标协会(以下简称中招协)2024年年会在宁波召开,北京筑龙作为中招协理事会员单位受邀出席会议。会议期间举行了“电子招标采购专业委员会换届会议暨第二届第一次工作会议”,北京筑龙当选第二届招标…

用代码给孩子造“钱”

起因 作为家里有两个娃的奶爸,时长为了孩子乱花钱而焦虑不已。然后最近看到一段短视频说了这么段话。 父母不要被动给孩子买东西,而是定期给孩子钱。让孩子自己管钱培养她对于钱的认知和理财的观念。 突然感觉大师我悟了。感觉值得一试。于是就打算给他…

如何在官网查看Qt5的所有模块?

2024年4月23日,周二上午 如果你不想一步步来的话,可以直接去这个Qt官方链接 https://doc.qt.io/qt-5/qtmodules.html 第一步:去到Qt官网 https://www.qt.io/ 第二步:点击文档链接 第三步:选择文档中的“Qt5” 第四步…

Python中的tkinter工具包帮助文档查询以及Python其他GUI工具包分类

Python中的tkinter工具包帮助文档查询以及Python其他GUI工具包分类 虽然Python支持许多GUI工具包,然而Tkinter是Python的实际标准GUI(图形用户界面)包,也是最常用的一种。本文简要介绍tkinter工具包帮助文档查询以及Python其他GU…

SpanBert学习

SpanBERT: Improving Pre-training by Representing and Predicting Spans 核心点 提出了更好的 Span Mask 方案,也再次展示了随机遮盖连续一段字要比随机遮盖掉分散字好;通过加入 Span Boundary Objective (SBO) 训练目标,增强了 BERT 的性…

Python小功能实现(链接下载图品并存储到EXCEL中)

import os import requests from openpyxl import Workbook from openpyxl.drawing.image import Image from concurrent.futures import ThreadPoolExecutor# 图片链接列表 image_urls ["https://uploads/file/20230205/f85Lpcv8PXrLAdmNUDE1Hh6xqkp0NHi2gSXeqyOb.png&q…

ctfshow——XSS

文章目录 XSS介绍什么是xss&#xff1f;XSS危害XSS的分类常用XSSpayload web316——反射型XSSweb317——过滤<script> web318——过滤script、imgweb319——不止过滤script、imgweb320——过滤空格web321——不止过滤空格web322——不止过滤空格web323web324web 325web32…

报名 | Qt汽车及工业行业解决方案及实战训练 深圳站(5月15日 星期三)

加入我们的Qt技术培训&#xff0c;探索跨平台应用开发的无限可能&#xff01;本次培训将深入Qt框架&#xff0c;涵盖从基础概念到高级功能的全方位知识&#xff0c;无论您是刚入门的新手还是希望提升技能的资深开发者&#xff0c;都能在此找到适合自己的学习路径。通过实践案例…

OpenTelemetry-2.Go接入Jaeger(grpc,gin-http)

目录 1.什么是OpenTelemetry 2.搭建jaeger 3.链路追踪 本地调用 远程调用 GRPC proto server端 client端 Gin-HTTP 调用流程 api1 api2 grpc 4.完整代码 1.什么是OpenTelemetry 参考&#xff1a;OpenTelemetry-1.介绍-CSDN博客 2.搭建jaeger 参考&#xff1a;…

齐护K210系列教程(八)_LCD显示图片

LCD显示图片 文章目录 LCD显示图片1&#xff0c;显示单张图片2&#xff0c;通过按键切换显示SD卡内的图片3&#xff0c;通过传感器切换图片4&#xff0c;画中画显示&#xff0c;并缩放5&#xff0c;课程资源 联系我们 AIstart 显示的图片的默认分辨率为&#xff1a;320*240 &am…

使用ROC指标100次盈利交易后,众汇才明白的道理

使用ROC指标100次盈利交易后才明白的道理&#xff0c;众汇外汇认为盈利的基本就是考虑这些指标。 ①.资产波动性 需要考虑到资产波动性&#xff0c;根据资产的波动性更改设置&#xff0c;设置的结果会告诉投资者这段时间的平均波动率。 ②添加过滤器。交易系统的主要指标是趋…

MySQL无法打开情况下读取frm文件的表结构

一、背景&#xff1a; 开发人员通过MySQL客户端工具&#xff0c;可以访问MySQL5.7.6&#xff0c;可以访问具体的DB&#xff0c;可以查看小写表的数据&#xff0c;但是无法查看大写表的数据&#xff0c;报错信息为“table does not exist”。 二、检查与分析&#xff1a; ssh登录…

圈子交友系统话题设置-免费圈子社区论坛交友系统-圈子交友系统功能介绍-APP小程序H5多端源码交付!

1. 圈子的独特创造与精心管理 源码赋予用户创造独特圈子的能力&#xff0c;为志同道合的人们打造一个分享兴趣、交流见解的平台。每个圈子都可以个性化定制主题、标签和规则&#xff0c;以确保圈子具备个性特点和强烈的社群感。作为圈子的创建者&#xff0c;您将享有自由编辑资…