vue --- 简单剖析数据驱动以及注意事项

本文深入探讨Vue.js框架中的数据驱动机制,详细解释了Getter/Setter的实现原理,以及Watcher如何监听变量变化并更新DOM。文章还列举了数组和对象在特定情况下无法触发更新的问题,并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天同事在开发的时候,对数组类型的数据进行赋值的时候发现视图并未更新。原来我也遇到过当时解决方式是改变赋值的方式,也未深究其原理,在以后的开发中可能因为开发习惯比较好,也再没出现过这种情况。今天就着这个问题剖析下vue的数据驱动,以及有哪些注意事项(文档中也说了,还是要经常刷文档,每刷一遍都有不同的理解)。

数据驱动的整体思路(可以这样理解,源码的实现过程比这里复杂很多):

  1. 对于声明的变量,vue在数据实例化的时候都会添加一个Getter和Setter,分别负责取值和赋值。
  2. 对于双向绑定的变量,也就是在DOM中绑定的变量,vue都会给他绑定一个Watcher。在赋值触发Setter的时候会触发Watcher,Watcher会重新计算变量的值是否发生了改变,在监听到变量发生变化时会操作具体的DOM改变其绑定的变量来实现更新。

下面就按照上面的过程分析下是如何实现的以及为什么会出现我同事出现的问题。

一、Getter/Setter

先讲下Getter/Setter:

// 简单实现
const data = {};
Object.defineProperty(data, 'names', {
  get: () => {
    console.log('获取数据');
    return ['Li', 'Zhang'];
  },
  set: (newVal) => {
    console.log('更新数据,参数是' + newVal);
  }
});

data.names; //获取数据
data.names = ['Wang']; //更新数据,参数是walsr (触发了set)
data.names.push = 'Wang'; //获取数据 (未触发set)

data相当于vue中的vm.$data,get是一个给names属性(相当于声明的names变量)提供 Getter 的方法,set是一个给属性提供 Setter 的方法(接受唯一参数)。触发set的时候继而触发了Watcher。到这里同时也回答了为什么vue不支持ie8及以下版本,因为vue用了defineProperty方法来实现监听数据,而defineProperty只支持IE9及以上版本浏览器。

看上面的例子大致可以明白我同事出现的问题在哪里,就是没有触发变量相应的set同时也就没触发变量相应的Watcher。这里我也有个疑问,在这个简单的例子中push并没有触发set,但vue是会监听到push的。官方文档数组更新检测说明,文档说了能监听到数组的变异方法,至于我同事的问题就是没有用对方法。在这里插入图片描述

二、Watcher

上面的整体思路正常理解起来没问题,但是跟vue源码具体实现Watcher还是有些区别的,我大致说一下我的理解吧,源码我理解的不是很全面,以下仅作参考。这里没有贴源码,只是我个人的理解,具体参考 Vue 源码解析:深入响应式原理

  1. 首先在vue实例化的时候会有一个生命周期,其中一个过程的就是调用 vm.initData() 来处理 data 选项。
  2. 在 initData 中使用了proxy 方法,它的功能就是遍历 data 的 key,把 data 上的属性(也就是在data中声明的变量)代理到 vm 实例上。 proxy 方法是通过 Object.defineProperty 的 Getter 和 Setter 方法实现了代理。这就是为什么会讲 Getter 和 Setter 。
  3. initData 在把变量代理到vm实例上后会调用 observe(data, this) 方法来对 data 做监听 。对于数组、对象类型的变量,observe会对其每一个属性进行转换,使它们都拥有 Getter、Setter 方法。到这里为止每个变量以及其属性都有了 Getter、Setter 方法,当变量被访问或者更新时,我们就可以追踪到这些变化。
  4. observe下面有一个 Dep 类,在 Getter 和 Setter 方法调用时会分别调用 dep.depend 方法和 dep.notify 方法,depend 方法的功能是把当前 Dep 的实例添加到当前正在计算的Watcher 的依赖中,notify 方法功能时遍历所有的 Watcher,调用相应的 update 方法,以此来达到更新的作用。

上述过程最重要的就是:通过observe(底层还是基于defineProperty)实现了变量和Watcher之间的关联,Watcher类再去实现相应DOM的更新。Watcher类的解析等我在深挖下源码在系统的梳理把,现在理解的可能不时很准确。

到这里基本上整个数据驱动流程就介绍完了,下面说下数据驱动的注意事项

三、注意事项

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c'],
    person: {
      name:'zhengsy'
    }
  }
})
  1. 数组:当你利用索引直接设置一个项时,例如:vm.items[0] = 'd'
  2. 数组:当你修改数组的长度时,例如:vm.items.length = 5
  3. 对象:当你添加或删除对象属性时:例如:vm.person.age = 33
  4. 对象:当你使用Object.assign()时:例如:Object.assign(vm.person, { age: 33 })

相应的解决办法:

  1. vm.$set(vm.items, 0, 'd')或者vm.items.splice(0, 1, 'd')
  2. vm.items.splice(5)
  3. vm.$set(vm.person, 'age', 33)
  4. vm.person = Object.assign({}, vm.person, { age: 33 })
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值