VUE双向绑定

前端碎碎念之vue双向绑定

本文章会详细介绍vue2和vue3的双向绑定


一、如何实现vue2的双向绑定

1.原理

其实vue2的双向绑定很简单,还记得ES5里面的Object.defineProperty( )这个方法吗?可以用这个方法来监听一个对象的属性,比如是否可以删除,是否可以枚举等,emmm跑题了,这里我们要说下setter和getter

var book = {
    _year : 2004,
    edition : 1
};
Object.defineProperty(book,"year",{ 
    get : function () {
        alert(this._year);
    },
    set : function (newValue) {
        if (newValue > 2004) {
            this._year = newValue;
            this.edition += newValue - 2004;
        }
    }
});
book.year;      // 结果:2004
book.year = 2005;       //设置的属性要和defineProperty()函数里面的属性相同
console.log(book.edition);   // 结果:2

这里我们定义了一个对象book,_year是一个访问器属性,当我们访问year时,会触发get函数,在修改时,会触发set函数
好了,基础用法知道了,我们看看vue2中怎么用这个方法吧
在vue2中,我们在data中定义了数据,在视图层用小胡子语法绑定时,会触发data对象中属性的get方法,在get方法中会触发addsub(),进行添加一个watcher,用来监听数据
在修改数据的时候,会触发set方法,在set方法中会首先对比前一个数据,如果有变化,就通知watcher自动触发render当前组件,生成新的虚拟dom树,然后记录差别,最后加载操作,局部修改到真实的dom树上面

2.vue2的双向数据绑定缺点:

1.无法监听对象内属性的增加和删除
2.无法监听数组变化,当直接使用索引(index)设置数组项时,不会被vue检测到
3.如果一个属性的层级太深,则会在引起性能的问题

3.解决方案:

1.对于缺点一,vue2提供了Vue.set(obj, key, value)和vue.delete(obj,key)
2.对于缺点二,vue重写了一些数组方法是,具体操作是首先执行原生的数据方法,对于push等方法通过switch选择后,进入不同case将数据变成响应式


const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
;[
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
].forEach(item=>{
	Object.defineProperty(arrayMethods,item,{
	    value:function mutator(){
	    	//缓存原生方法,之后调用
	    	const original = arrayProto[item]	
	    	let args = Array.from(arguments)
		    original.apply(this,args)
	    },
	})
})
function protoAugment (target,src) {
  target.__proto__ = src
}
// 调用
let obarr = []
protoAugment(obarr, arrayMethods)

经过以上的代码可以看出,只会更改我们给定数组(obarr)的相关方法,而不会污染Array的原生方法,因此其他普通数组不受影响。对于不支持__proto__属性的浏览器,直接使用Object.defineProperty从新定义相关属性。Vue的实现方法正如上,更改我们需要监听的Array数组属性值(属性值为函数),在监听函数里执行数组的原生方法,并通知所有注册的观察者进行响应式处理。

二、vue3数据双向绑定

1.原理

vue3双向绑定的实现是基于es6的proxy生成的,基于proxy生成一个代理对象,在创建vue实例时,会返回一个proxy代理
proxy如何实现响应式的数据响应绑定呢?
在reactive函数中,会通过传入一个target来实现响应,在vue定义时的data会作为target传入,这个函数返回一个代理,通过代理修改值时会触发trigger更新,同时reactive如果发现target是对象,会进行拦截,如果是就会通过newproxy( target,handler),当操作代理对象时会触发handler方法handler中可放一个set(),get(),deleteproprty()当获取属性是,触发get(),修改属性时触发set,删除时触发delete()在get中收集依赖,set中会触发依赖,如果target是一个对象的话,会递归调用reactive去产生代理,同时为了防止重复代理,其实还创建了一个哈希表,在reactive中,会查看哈希表,如果存在,直接return,无需再次代理,如果没有,就加入

2.优点

vue中监听的是属性,而vue3中监听的对象,因此可以解决缺点一和二,同时在数据层级太深是vue3是需要时才会进行递归,不会太损耗性能

三、如何实现依赖收集(vue2为例)

观察者模式是一种实现一对多关系解耦的行为设计模式。它主要涉及两个角色:观察目标、观察者。Vue源码中实现依赖收集,实现了三个类: - Dep:扮演观察目标的角色,每一个数据都会有Dep类实例,它内部有个subs队列,subs就是subscribers的意思,保存着依赖本数据的观察者,当本数据变更时,调用dep.notify()通知观察者 - Watcher:扮演观察者的角色,进行观察者函数的包装处理。如render()函数,会被进行包装成一个Watcher实例 - Observer:辅助的可观测类,数组/对象通过它的转化,可成为可观测数据

1.dep

Dep类实例依附于每个数据而出来,用来管理依赖数据的Watcher类实例,在取或改某一数据时,可访问dep.target,在后面的依赖收集部分中getter中会调用dep.depend()而setter会调用dep.notify()
假如有一组数据:

{
    a: 1,
    b: [2, 3, 4],
    c: {
        d: 5
    }
}

添加完数据观测会变成

{
    __ob__, // Observer类的实例
    a: 1,  
    b: [2, 3, 4], 
    c: {
        __ob__, // Observer类的实例
        d: 5 
    }
}

我们发现,对象有了_ob_,其实_ob_上的Observer是将对象变成了响应式, 将Observer类的实例挂载在__ob__属性上,提供后续观测数据使用,以及避免被重复实例化。然后,实例化Dep类实例,并且将对象/数组作为value属性保存下来 ,通过判断是对象还是数组,对象执行walk()把每一项属性都变为可观测数据,数组就执observeArray()过程,递归地对数组元素调用observe(),处理元素还是数组的情况

2.watcher

Watcher扮演的角色是观察者,它关心数据,在数据变化后能够获得通知,并作出处理。一个组件里可以有多个Watcher类实例
在Watcher类里做的事情,概括起来则是:
1、 deps:缓存上一轮执行观察者函数用到的dep实例 - depIds:Hash表,用于快速查找 - newDeps:存储本轮执行观察者函数用到的dep实例 - newDepIds:Hash表,用于快速查找
2、进行初始求值watcher.get()方法
3、在初始准备工作里,会将当前Watcher实例赋给Dep.target,清空数组newDeps、newDepIds,Dep.target指向dep
4、触发数据的getter,从而执行watcher.addDep()方法,将特定的数据记为依赖
5、对每个数据执行watcher.addDep(dep)后,数据对应的dep如果在newDeps里不存在,就会加入到newDeps里,保证同样的依赖只能收集一次。并且如果在deps不存在,表示上一轮计算中,当前watcher未依赖过某个数据,
6、首先释放Dep.target,然后拿newDeps和deps进行对比,接着进行以下的处理: 如果newDeps里不存在,deps里存在的数据,则是过期的缓存数据。dep.subs移除掉当前watcher - 将newDeps赋给deps,表示缓存本轮的计算结果
8、进行了set时,对dep.subs这一观察者队列里的watchers进行通知,触发dep.notify(),从而执行watcher.update()方法,update()会重复以上


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue双向绑定原理是通过数据劫持和发布订阅模式相结合的方式来实现的。在Vue中,当用户操作View时,ViewModel会感知到变化并通知Model进行相应的改变;反之,当Model发生改变时,ViewModel也能感知到变化并使View作出相应的更新。双向绑定的核心是使用了Object.defineProperty()方法来实现。 在Vue的初始化过程中,会对data数据进行劫持监听,这个过程由监听器Observe来完成。监听器会监听所有属性,当属性发生变化时,会通知订阅者Watcher来判断是否需要更新。由于订阅者Watcher可能有多个,所以需要一个消息订阅器Dep来统一管理这些订阅者。同时,还需要一个指令解析器Compile,用来扫描和解析每个节点的相关指令,将其初始化为一个订阅者Watcher,并替换模板数据或绑定相应的函数。 当订阅者Watcher接收到属性的变化通知时,会执行对应的更新函数,从而更新视图。整个过程中,监听器Observer负责劫持并监听所有属性,订阅者Watcher负责接收属性的变化通知并执行相应的函数,消息订阅器Dep负责收集订阅者并通知Watcher触发更新,指令解析器Compile负责扫描和解析节点的指令并初始化相应的订阅者。 综上所述,Vue双向绑定原理是通过数据劫持+发布订阅模式相结合的方式来实现的,通过监听器、订阅者、消息订阅器和指令解析器等组件的协作,实现了数据和视图之间的双向绑定。 #### 引用[.reference_title] - *1* *2* [vue双向绑定原理](https://blog.csdn.net/qq_41645323/article/details/123324680)[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^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Vue双向绑定原理](https://blog.csdn.net/weixin_52092151/article/details/119810514)[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^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值