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>Document</title>
</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>

在这里插入图片描述

那么,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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue数据响应原理是通过结合数据劫持发布-订阅者模式来实现的。Vue使用Object.defineProperty来劫持data中各个属性的setter和getter方法,并在数据变动时发布消息给订阅者,从而触发相应的监听回调。 具体来说,Vue会创建一个observe对象来对数据进行劫持,将数据对象的每个属性都转换为getter和setter。当数据发生变动时,setter会被调用,然后发布消息给订阅者,通知它们数据已经发生改变了。订阅者会触发相应的监听回调进行相应的更新操作。 在Vue中,每个组件实例都会对应一个watcher实例。当组件渲染过程中使用了数据属性时,watcher会将这些属性收集为依赖。当依赖项的setter被触发时,watcher会被通知,从而使其关联的组件重新渲染。这样就实现数据响应式更新。 总结起来,Vue数据响应原理是通过数据劫持和发布-订阅者模式的结合来实现的,利用Object.defineProperty来劫持数据属性的setter和getter方法,然后通过发布-订阅者模式来实现数据变动时的通知和更新操作。这种机制使得Vue能够实现高效的数据更新和视图渲染。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [vue响应原理](https://blog.csdn.net/dongqian911/article/details/116242827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [vue响应原理](https://blog.csdn.net/weixin_48181168/article/details/120158346)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值