Vue2响应式系统之怎么让数组生效(vue,开发技术)

时间:2024-05-09 21:33:58 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

1、场景

import{observe}from"./reactive";importWatcherfrom"./watcher";constdata={list:["hello"],};observe(data);constupdateComponent=()=>{for(constitemofdata.list){console.log(item);}};newWatcher(updateComponent);data.list=["hello","liang"];

先可以一分钟思考下会输出什么。

虽然的值是数组,但我们是对进行整体赋值,所以依旧会触发的,触发进行重新执行,输出如下:listdata.listdata.listsetWatcher

Vue2响应式系统之怎么让数组生效

2、场景 2

import{observe}from"./reactive";importWatcherfrom"./watcher";constdata={list:["hello"],};observe(data);constupdateComponent=()=>{for(constitemofdata.list){console.log(item);}};newWatcher(updateComponent);data.list.push("liang");

先可以一分钟思考下会输出什么。

这次是调用方法,但我们对方法什么都没做,因此就不会触发了。pushpushWatcher

3、方案

为了让 还有数组的其他方法也生效,我们需要去重写它们,通过push代理模式我们可以将数组的原方法先保存起来,然后执行,并且加上自己额外的操作。

/**nottypecheckingthisfilebecauseflowdoesn'tplaywellwith*dynamicallyaccessingmethodsonArrayprototype*//*exportfunctiondef(obj,key,val,enumerable){Object.defineProperty(obj,key,{value:val,enumerable:!!enumerable,writable:true,configurable:true,});}*/import{def}from"./util";constarrayProto=Array.prototype;exportconstarrayMethods=Object.create(arrayProto);constmethodsToPatch=["push","pop","shift","unshift","splice","sort","reverse",];/***Interceptmutatingmethodsandemitevents*/methodsToPatch.forEach(function(method){//cacheoriginalmethodconstoriginal=arrayProto[method];def(arrayMethods,method,functionmutator(...args){constresult=original.apply(this,args);/*****************这里相当于调用了对象set需要通知watcher************************///待补充/*****************************************************************************/returnresult;});});

当调用了数组的或者其他方法,就相当于我们之前重写属性的,上边待补充的地方需要做的就是通知中的。pushsetdepWatcher

exportfunctiondefineReactive(obj,key,val,shallow){constproperty=Object.getOwnPropertyDescriptor(obj,key);//读取用户可能自己定义了的get、setconstgetter=property&&property.get;constsetter=property&&property.set;//val没有传进来话进行手动赋值if((!getter||setter)&&arguments.length===2){val=obj[key];}constdep=newDep();//持有一个Dep对象,用来保存所有依赖于该变量的WatcherletchildOb=!shallow&&observe(val);Object.defineProperty(obj,key,{enumerable:true,configurable:true,get:functionreactiveGetter(){constvalue=getter?getter.call(obj):val;if(Dep.target){dep.depend();}returnvalue;},set:functionreactiveSetter(newVal){constvalue=getter?getter.call(obj):val;if(setter){setter.call(obj,newVal);}else{val=newVal;}dep.notify();},});}

如上边的代码,之前的是通过闭包,每一个属性都有一个各自的,负责收集和通知。depdepWatcherWatcher

那么对于数组的话,我们的放到哪里比较简单呢?dep

回忆一下现在的结构。

constdata={list:["hello"],};observe(data);constupdateComponent=()=>{for(constitemofdata.list){console.log(item);}};newWatcher(updateComponent);

上边的代码执行过后会是下图的结构。

Vue2响应式系统之怎么让数组生效

list属性在闭包中拥有了属性,通过,收集到了包含函数的。Depnew WatcherupdateCompnentWatcher

同时因为的是数组,也就是对象,通过上篇listvalue["hello"]响应式系统之深度响应(opens new window)我们知道,它也会去调用函数。Observer

那么,我是不是在中也加一个就可以了。ObserverDep

Vue2响应式系统之怎么让数组生效

这样当我们调用数组方法去修改的值的时候,去通知中的就可以了。['hello']ObserverDep

3、收集依赖代码实现

按照上边的思路,完善一下类。Observer

exportclassObserver{constructor(value){/******新增*************************/this.dep=newDep();/************************************/ this.walk(value);}/***遍历对象所有的属性,调用defineReactive*拦截对象属性的get和set方法*/walk(obj){constkeys=Object.keys(obj);for(leti=0;i<keys.length;i++){defineReactive(obj,keys[i]);}}}

然后在中,当前中的也去收集依赖。getOberverdep

exportfunctiondefineReactive(obj,key,val,shallow){constproperty=Object.getOwnPropertyDescriptor(obj,key);//读取用户可能自己定义了的get、setconstgetter=property&&property.get;constsetter=property&&property.set;//val没有传进来话进行手动赋值if((!getter||setter)&&arguments.length===2){val=obj[key];}constdep=newDep();//持有一个Dep对象,用来保存所有依赖于该变量的WatcherletchildOb=!shallow&&observe(val);Object.defineProperty(obj,key,{enumerable:true,configurable:true,get:functionreactiveGetter(){constvalue=getter?getter.call(obj):val;if(Dep.target){dep.depend();/******新增*************************/if(childOb){//当前value是数组,去收集依赖if(Array.isArray(value)){childOb.dep.depend();}}/************************************/}returnvalue;},set:functionreactiveSetter(newVal){constvalue=getter?getter.call(obj):val;if(setter){setter.call(obj,newVal);}else{val=newVal;}dep.notify();},});}

4、通知依赖代码实现

我们已经重写了方法,但直接覆盖全局的方法肯定是不好的,我们可以在类中去操作,如果当前是数组,就去拦截它的方法。arrayarrrayObservervaluearray

这里就回到的原型链上了,我们可以通过浏览器自带的,将当前对象的原型指向我们重写过的方法即可。js__proto__

考虑兼容性的问题,如果不存在,我们直接将重写过的方法复制给当前对象即可。__proto__

import{arrayMethods}from'./array'//上边重写的所有数组方法/*exportconsthasProto="__proto__"in{};*/exportclassObserver{constructor(value){this.dep=newDep(); /******新增*************************/if(Array.isArray(value)){if(hasProto){protoAugment(value,arrayMethods);}else{copyAugment(value,arrayMethods,arrayKeys);}/************************************/}else{this.walk(value);}}/***遍历对象所有的属性,调用defineReactive*拦截对象属性的get和set方法*/walk(obj){constkeys=Object.keys(obj);for(leti=0;i<keys.length;i++){defineReactive(obj,keys[i]);}}}/***AugmentatargetObjectorArraybyintercepting*theprototypechainusing__proto__*/functionprotoAugment(target,src){/*eslint-disableno-proto*/target.__proto__=src;/*eslint-enableno-proto*/}/***AugmentatargetObjectorArraybydefining*hiddenproperties.*//*istanbulignorenext*/functioncopyAugment(target,src,keys){for(leti=0,l=keys.length;i<l;i++){constkey=keys[i];def(target,key,src[key]);}}

还需要考虑一点,数组方法中我们只能拿到值,那么怎么拿到对应的呢。valuevalueObserver

我们只需要在类中,增加一个属性来指向自身即可。Observe

exportclassObserver{constructor(value){this.dep=newDep();/******新增*************************/def(value,'__ob__',this)/************************************/if(Array.isArray(value)){if(hasProto){protoAugment(value,arrayMethods);}else{copyAugment(value,arrayMethods,arrayKeys);}}else{this.walk(value);}} ...}

回到最开始重写的方法中,只需要从中拿到去通知即可。array__ob__DepWatcher

/**nottypecheckingthisfilebecauseflowdoesn'tplaywellwith*dynamicallyaccessingmethodsonArrayprototype*/import{def}from"./util";constarrayProto=Array.prototype;exportconstarrayMethods=Object.create(arrayProto);constmethodsToPatch=["push","pop","shift","unshift","splice","sort","reverse",];/***Interceptmutatingmethodsandemitevents*/methodsToPatch.forEach(function(method){//cacheoriginalmethodconstoriginal=arrayProto[method];def(arrayMethods,method,functionmutator(...args){constresult=original.apply(this,args);/*****************这里相当于调用了对象set需要通知watcher************************/constob=this.__ob__;//notifychangeob.dep.notify();/*****************************************************************************/returnresult;});});

5、测试

import{observe}from"./reactive";importWatcherfrom"./watcher";constdata={list:["hello"],};observe(data);constupdateComponent=()=>{for(constitemofdata.list){console.log(item);}};newWatcher(updateComponent);data.list.push("liang");

这样当调用方法的时候,就会触发相应的来执行函数了。pushWatcherupdateComponent

当前的依赖就变成了下边的样子:

Vue2响应式系统之怎么让数组生效

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:Vue2响应式系统之怎么让数组生效的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:Vue2响应式系统之深度响应怎么实现下一篇:

15 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18