• Vue数据响应原理及Diff比对



    1. Vue数据响应原理

    Vue在数据响应的过程中会进行数据劫持,通过比对的方式(就是判断要更新的数据是标签还是属性等等,然后我应该怎么去做)更新数据。下面的代码用传统 js 操作 Dom 的方式简单实现了这一原理。

    DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Documenttitle>
    head>
    
    <body>
      <h3 id="title">h3>
      <input type="text" id="username">
    
      <script>
        // 数据源
        const data = {
          name: '张三'
        }
        // 目标数据
        const target = {}
        // 响应数据 (数据劫持)
        /*   
            Object.defineProperty() 方法会直接在一个对象上定义一个新属性,
            或者修改一个对象的现有属性,并返回此对象。
            obj 要定义属性的对象。
            prop 要定义或修改的属性的名称或 Symbol 。
            descriptor 要定义或修改的属性描述符。 
        */
        Object.defineProperty(target, 'name', {
          // 获取  相当于用Dom方式获取target.name
          get() {
            // return Reflect.get(data, 'name') 反射写法:这一种可以捕获异常
            return data.name
          },
          set(newValue) {
            // 视图改变导致数据改变
            data.name = newValue
            // 通知页面元素显示的数据改变
            document.getElementById('title').textContent = newValue
          }
        })
    
        // 获取dom中的元素,使得输入框和h3标题中的值都是数据源中的数据
        const username = document.getElementById('username')
        username.value = target.name
        const title = document.getElementById('title')
        title.textContent = target.name
    
        // 页面改变导致数据改变
        username.addEventListener('input', function () {
          target.name = this.value
        })
    
      script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    在这里插入图片描述

    那么,Vue究竟是怎么实现数据响应的呢?

    当把一个普通的JavaScript对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,使用Object.defineProperty(vue2.x),vue3.x 中使用了 Proxy 类把这些属性全部转为 getter/setter (数据劫持)。在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。下图可以帮助理解。

    在这里插入图片描述

    Vue 的数据劫持手段:

    1. 对于一般属性数据进行劫持,通过键值对声明的方式
    2. 对于多层级中的数据,进行递归来完成劫持
    3. 对于数组中的数据,进行重写对应的相关方法来完成劫持

    补充:

    在 Vue 中如果你不想让一个数据变为响应性的,可以把它冻结起来Object.freeze({id:1})。这样可以让性能得到提升。

    检查当时对象是否冻结:Object.isFrozen()

    2. Diff比对

    vue中当数据发生改变的时候,对应监听的set方法会执行,调用数据中的Dep.notify方法通知所有的订阅者,订阅者就会通过patch函数对比新旧虚拟节点是否一样,如果用新的虚拟节点则整个替换老节点,如果不是使用新节点,则根据子节点情况来进行同层比较。

    在这里插入图片描述

    patch主要做4个判断

    1. 没有新节点,直接触发旧节点的destory钩子,进行销毁。

      假如旧节点中存在 a 节点,而新节点中没有 a 节点,则直接将旧节点中的 a 节点销毁。

    2. 没有旧节点,此时根本不需要比较了,直接全部都是新建

    3. 旧节点和新节点一样时,直接调用 patchVnode 去处理这两个节点

    4. 旧节点和新节点自身不一样,当两个节点不一样的时候,直接创建新节点,删除旧节点

    patch 做的是同层比对(同层比对复杂度较低,可以提升性能),同层比对后进行同 key 比对。当没有 key 时,就按顺序比。

    没有key的比较:

    在这里插入图片描述

    比对时有key值标识:

    在这里插入图片描述

    key值的作用:在使用diff算法比较新旧dom树的时候,可以更准确更快得找到oldDom树中对应的节点。(利用key的唯一性生成map对象来获取对应节点,比遍历方式更快)

    关于 Diff 比对的算法,这篇文章通俗易懂,很好理解,特此推荐:

    https://blog.csdn.net/weixin_43638968/article/details/112686317

  • 相关阅读:
    mysql事务和事务隔离级别
    17. 电话号码的字母组合
    RobotFramework 读取Excel文件
    【python获取.doc内表格指定单元格数据】
    TorchDrug教程--知识图推理
    8、Docker-compose容器编排
    Java后端开发(六)-- 二维码的生成
    fundamental notes in 3D math
    MATLAB算法实战应用案例精讲-【智能优化算法】算术优化算法-AOA(附matlab代码)
    Aurora中的策略模式和模板模式
  • 原文地址:https://blog.csdn.net/weixin_45605541/article/details/126653024