vue3源码正式放出来了,想必大家也都开始争先恐后的学习vue3的知识了。由于vue3已经不再支持v模型了,而使用.sync来代替,但是为了这篇文章可以帮助大家快速了解vue的双向绑定实现原理,部分使用了vue2。x v模型的实现原理
代理的基础知识,相信大家已经都很了解了,让我们一起来回顾一下吧
代理是对一个对象的代理,并返回一个已代理的对象,已代理的对象如果发生任何设置跟获取的方法都可以被捕获到,我们写一个简单的:栗:
const目标={ 答:1 } const={回身 get () {//当对观察到的。一进行取值时会触发 }, 集(){//当对观察到的。一进行赋值时会触发 },//还有一些额外的参数如等,这里用不到,就不多说了 … } const观察=new代理(目标)回身 >之前这样我们就可以对目标对象设置了一层代理,当我们对目标进行取赋值操作的时候就可以接可以截获到它的行为了,但是如果你以为就只有这么简单你就错了。
我们把目标改写成多层嵌套
const目标={ 答:{ b: 1 } } … const观察=new代理(目标)回身 >之前我们再获取observed.a。b=2的时候,方法取到的是一个的值{b: 1},而设置并不会触发。这也说明了代理只能代理一层对象,不能深层代理!
那么我们需要监听到嵌套的对象怎么办?
其实这个也不难,就是在得到的时候判断一下得到的值是不是对象,如果是对象的话就在对它代理一层,直到最后一层,全部代理完为止,这里就需要一个递归函数
const目标={ 答:{ b: 1 } } 功能活性(数据:有){ const={回身 (目标、关键、接收机){ const res=反映。get(目标、关键、接收机); 如果(isObject (res)) { 数据(关键)=活性(res); } 返回目标(关键); } } const观察=new代理(目标)回身 } >之前这样我们就可以对目标函数内部的所有属性进行深层监听了,但是这样还是不够,因为我们每次取值的时候都会设置代理这样会导致代码无限循环→死循环,所以我们需要做一层判断,如果已经设置了代理的或这已经是代理的对象就不需要在此设置代理了。又因为我们要储存对象的映射,所以需要使用地图函数。下面是反应完整的代码。
const rawToReactive: WeakMap<任何any>=new WeakMap (); const reactiveToRaw: WeakMap<任何any>=new WeakMap (); 功能活性(数据:有){//已经有代理 我们观察到=rawToReactive.get(数据); 如果观察到!==无效0){ 返回观察; }//这个数据已经是代理 如果(reactiveToRaw.has(数据)){ 返回数据; } 常量处理程序={ 得到:函数(目标:任何,关键:字符串,接收机:有){ const res=反映。get(目标、关键、接收机); 如果(isObject (res)) { (例子)=数据(关键)=活性(res); } 返回目标(关键); }, 设置:函数(目标:任何关键:字符串,价值:有){//将新值赋值 目标(关键)=价值;//通知所有订阅者触发更新 触发(目标);//严格模式下需要设置返回值,否则会报的错 返回值; } };//返回代理监听对象 观察=new代理(数据处理程序一样); rawToReactive。(数据、观察); reactiveToRaw。组(观察、数据); 返回观察; } >之前定义观察者用来作为编译跟活性的桥梁,跟vue3的实现可能不一样
//收集观察家依赖 const Dep: Dep={ deps: [], 添加(观察者:观察者){ this.deps.push(观察者); } };//观察者跟编译的桥梁,在编译时添加观察者,在数据更新时触发更新更新视图 函数_watcher(节点:任何,attr:字符串,数据:任何,关键:字符串):观察者{ 返回{ 节点, attr, 数据, 键, update () {//逐层取值 const mutationKeys=this.key.split ('。'); 如果(mutationKeys。长度比;1){ 让d:任何=零; mutationKeys。forEach(关键=比;(d=J?关键)| | (d,,d(例子)))); this.node(这一点。attr=d; 返回; } this.node(这一点。attr]=this.data [this.key]; } }; }vue3实现v模型原理详解