Vue2响应式系统之异步队列怎么实现
导读:本文共2247.5字符,通常情况下阅读需要7分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要: 试想一下如果这里的 console.log 是渲染页面,那改变一次值就刷新一下页面,会造成严重的性能问题,页面也会不停的改变。场景import{observe}from"./reactive";importWatcherfrom"./watcher";constdata={a:1,b:2,c:3,};obs... ...
目录
(为您整理了一些要点),点击可以直达。试想一下如果这里的 console.log 是渲染页面,那改变一次值就刷新一下页面,会造成严重的性能问题,页面也会不停的改变。
场景
import{observe}from"./reactive";importWatcherfrom"./watcher";constdata={a:1,b:2,c:3,};observe(data);constupdateComponent=()=>{console.log(data.a+data.b);};newWatcher(updateComponent);constupdateComponent2=()=>{console.log(data.c);};newWatcher(updateComponent2);data.a=2;data.a=3;data.b=4;data.c=5;
new Watcher
(updateComponent) 进行依赖收集会输出一次 3 ,new Watcher
(updateComponent2) 进行依赖收集也会输出一次 3 。
之后我们依次改变 a、 a 、b、c 的值,每改变一次就会触发 Watcher 的执行,会连续进行四次的 console.log。
试想一下如果这里的 console.log 是渲染页面,那改变一次值就刷新一下页面,会造成严重的性能问题,页面也会不停的改变。
解决方案
我们可以通过一个队列,收集所有的 Watcher 。
那什么时候执行 Watcher 队列呢?
为了等所有的 Watcher 都收集完毕,可以将 Watcher 的执行放到 setTimeout 中。这样当主线程全部执行后,才会去执行 Watcher 队列。
代码实现
我们可以给每一个 Watcher 加上一个 id,如果队列中已经有 id 了就不加入队列。
letuid=0;exportdefaultclassWatcher{constructor(Fn,options){this.getter=Fn;this.depIds=newSet();//拥有has函数可以判断是否存在某个idthis.deps=[];this.newDeps=[];//记录新一次的依赖this.newDepIds=newSet();/******新增*************************/this.id=++uid;//uidforbatching//optionsif(options){this.sync=!!options.sync;}/************************************/this.get();}...}
我们同时提供了一个 options 对象,保存了其中的 sync 字段,表示是像之前一样立即出触发 Watcher 还是放到队列中。
然后 Watcher 的 update 方法中我们去调用加入队列的函数。
exportdefaultclassWatcher{...update(){if(this.sync){this.run();//直接运行}else{queueWatcher(this);//加入队列}}...}
看一下 queueWatcher
的实现。
constqueue=[];//保存Watcher队列lethas={};//去重Watcherletwaiting=false;//是否加入到了setTimeout队列exportfunctionqueueWatcher(watcher){constid=watcher.id;if(has[id]==null){has[id]=true;queue.push(watcher);//加入队列//queuetheflushif(!waiting){//执行Watcher函数放到setTimeout队列中,只加入一次即可waiting=true;setTimeout(flushSchedulerQueue,0);}}}
再看一下上边执行 Watcher
队列的 flushSchedulerQueue 函数的实现。
letflushing=false;//是否正在执行队列letindex=0;/***Flushbothqueuesandrunthewatchers.*/functionflushSchedulerQueue(){flushing=true;letwatcher,id;for(index=0;index<queue.length;index++){watcher=queue[index];id=watcher.id;has[id]=null;watcher.run();}resetSchedulerState();//执行结束后进行重置}/***Resetthescheduler'sstate.*/functionresetSchedulerState(){index=queue.length=0;has={};waiting=flushing=false;}
总体上就是上边的样子了。
执行结果
import{observe}from"./reactive";importWatcherfrom"./watcher";constdata={a:1,b:2,c:3,};observe(data);constupdateComponent=()=>{console.log(data.a+data.b);};newWatcher(updateComponent);constupdateComponent2=()=>{console.log(data.c);};newWatcher(updateComponent2);data.a=2;data.a=3;data.b=4;data.c=5;
虽然后边我们改变了四次 data 中的值,但事实上只有两个 Watcher ,因此只会输出两次。
</div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
Vue2响应式系统之异步队列怎么实现的详细内容,希望对您有所帮助,信息来源于网络。