数据代理、数据劫持;vue响应式与双向绑定

数据代理 Object.defineProperty

vm.xxx(data配置的数据)=vm. _ data.xxx
通过vm.xxx代理对vm. _ data.xxx的操作(读/写),这样通过vm直接获取数据的时候就调用getter,获取_ data中的值,当修改的时候调用setter修改 _ data 中的值

vue中vm(vue实例)对data内的全部属性进行了数据代理。
Vue中数据代理的好处:更加方便的操作data(模型-Model)中的数据。
既然vm上挂的属性就是_data中的数据代理,那么{{vm._data.name}}和{{name}}是等价的,{{vm_data.name='szk2'}}和{{name='szk2'}}也是等价的
所以就是为了写代码的方便,在{{}}直接写数据,或者直接修改就能操作到_data中

数据劫持Observer

数据劫持,指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果

_data里做的数据劫持,将vue代码里我们写的data加工了一下,让每个属性有了getter和setter
让vue做到data里的数据改变后马上做到页面更新
在这里插入图片描述
实际上vue通过监听者observer来监听data中的数据,这个getter和setter就是监听者里面的方法

getter就是监听者获取data中数据的,setter则是监听当数据发生变化的时候执行操作的

当修改属性的时候,setter被调用,在setter方法中就会让订阅者执行重新解析模板的操作,从而改变了页面
在这里插入图片描述

vue响应式

数据驱动视图,我们修改数据视图随之响应更新。

Vue2.x是借助Object.defineProperty()对数据进行劫持同时结合发布-订阅者模式(==
通过Object.defineProperty()来劫持data中各个属性的setter、getter,在数据变动时,发布消息给订阅者,触发响应的监听回调。==),而Vue3.x是借助Proxy实现的。

Vue 利用 Object.defineProperty 创建一个 observer 来劫持监听所有的属性,把这些属性全部转为 getter 和 setter
Vue 中每个组件实例都会对应一个 watcher 实例,它会在组件渲染的过程中把使用过的数据属性通过 getter 收集为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染

发布-订阅者模式

发布-订阅者模式的作用:可处理一对多的场景,应用于不同情况下的不同函数调用

vue.js采用数据劫持结合发布-订阅者模式,通过Object.defineProperty()来劫持data中各个属性的setter、getter,在数据变动时,发布消息给订阅者,触发响应的监听回调

其实一种多对一的关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
在这里插入图片描述

发布者observe与订阅者watcher

observe是一个发布者,执行了发布者的权利,增加了订阅者,并且在改变时通知了订阅者。

数据劫持data各个属性的setter与getter,数据变动后发布消息给订阅者,触发监听回调
详细可见Vue响应式原理探究之“发布-订阅”模式

vue2响应式的局限

Vue 不能检测数组对象的变化
对象
Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以只有在初始化实例时就存在于data的property才是响应式的,那么就无法检测 property 的添加或移除
解决:
Vue.set(object, propertyName, value) 方法或者其别名vm.$set嵌套对象添加响应式 property。
(因为vue不允许动态添加根级别的响应式 property)

数组
vue不能检测以下数组的变动:

当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength,对数组进行push、pop 等操作都不会触发更新

解决:重写数组原型上的方法同时不污染全局

定义监听数组的原型
主要分为三步:

第一步:创建一个对象,将数组的原型赋值给该对象

const oldArrayProperty = Array.prototype

第二步:创建新对象,原型指向该对象

const arrProperty = Object.create(oldArrayProperty)

第三步:重写该对象上的方法

arrProperty.push = function(){} ...
arrProperty.pop = function(){} ...
// 重新定义数组原型,加入触发更新的机制
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向oldArrayProperty
const arrProperty = Object.create(oldArrayProperty)
// 重写原型上的方法(可以所有都重写,这里只进行少量举例)
// arrProperty.push = function(){} 
// arrProperty.pop = function(){}
// 优化写法
const methods = ['push','pop','shift','unshift','splice']
methods.forEach(method => {
  arrProperty[method] = function(){
    updateView()
    Array.prototype[method].call(this, ...arguments)
  }
})

将需要监听的数组原型指向自定义的特殊原型
对原来的 observe 进行修改,加入数组判断,如果是数组则修改该数组的原型,至此,数组监听完成,下面是 observe 修改后代码以及测试例子

// 监听对象属性
function observe(target){
  if(typeof target !== 'object' || target === null) {
    // 不是数组或对象
    return target
  }
  // 如果是数组则修改该数组的原型
  if(Array.isArray(target)){
    target.__proto__ = arrProperty
    return
  }

  // 重新定义属性
  for(let key in target) {
    defineReactive(target, key, target[key])
  }
}

// 测试数据
const data = {
  myCars: ['Bugatti','Koenigsegg']
}

// 监听数据
observe(data)

// 测试
data.myCars.push('AE86') // (监听成功)输出 --> 数据更新

v-model双向绑定

 <input type="text" :value="testMessage" @input="testMessage = $event.target.value">
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值