本文转载自微信公众号《前端日志》,作者前端日志。转载本文请联系前端日志公众号。本文主要分析了Vue从更新Data到通知Watcher异步更新视图的过程,也就是下图中橙色部分。先回顾一下图中的对象:Data对象:Vue中data方法返回的对象。Dep对象:每个Data属性都会创建一个Dep来收集所有使用这个Data的Watcher对象。Watcher对象:主要用来渲染DOM。接下来,我们开始分析这个过程。Vue异步更新DOM原理很多同学都知道Vue中的数据更新是异步的,也就是说我们修改Data之后,并不能立即获取到修改后的DOM元素。例如:{{message}}
我们什么时候才能拿到真正的DOM元素?答:在Vue的nextTick回调中。这一点在Vue官网上有详细的介绍,但是大家有没有想过为什么Vue需要使用nextTick方法来获取最新的DOM呢?带着这个疑问,我们来看一下源码。//当一个Data被更新时,会依次执行下面的代码//1.触发器数据集//2。调用dep.notify//3.Dep会遍历所有相关的Watcher,并执行更新方法classWatcher{//4。执行更新操作update(){queueWatcher(this);}}constqueue=[];functionqueueWatcher(watcher:Watcher){//5.将当前Watcher添加到异步队列queue.push(watcher);//6.执行异步队列,并传入回调nextTick(flushSchedulerQueue);}//更新视图函数的具体方法flushSchedulerQueue(){letwatcher,id;//排序,先渲染父节点,再渲染子节点//这样可以避免不必要的子节点渲染,比如:父节点中v-if为false的子节点不需要渲染queue.sort((a,b)=>a.id-b.id);//遍历所有Watcher进行批量更新。for(index=0;index{cb.call(ctx);});//2.执行异步任务//该方法会根据浏览器兼容性选择不同的异步策略timerFunc();}可以看到nextTick函数很简单,只是将传入的flushSchedulerQueue添加到callbacks数组中,然后执行timerFunc方法。接下来我们分析一下timerFunc方法。lettimerFunc;//判断是否兼容Promiseif(typeofPromise!=="undefined"){timerFunc=()=>{Promise.resolve().then(flushCallbacks);};//判断是否兼容MutationObserver//https://https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver}elseif(typeofMutationObserver!=="undefined"){letcounter=1;constobserver=newMutationObserver(flushCallbacks);consttextNode=document.createTextNode(String(counter));observer.observe(textNode,{characterData:true,});timerFunc=()=>{counter=(counter+1)%2;textNode.data=String(counter);};//判断是否兼容setImmediate//部分IE浏览器存在该方法}elseif(typeofsetImmediate!=="undefined"){//这是一个宏任务,但比setTimeouttimerFunc=()=>{setImmediate(flushCallbacks);};}else{//如果不知道以上方法,使用setTimeout0timerFunc=()=>{setTimeout(flushCallbacks,0);};}//异步执行后,执行所有的回调方法,即执行flushSchedulerQueuefunctionflushCallbacks(){for(leti=0;i{consttextContent=document.getElementById("text").textContent;console.log(textContent==="helloworld");//true});setTimeout(()=>{consttextContent=document.getElementById("text").textContent;console.log(textContent==="helloworld");//真的});思考与总结本文从源码的角度介绍了Vue的异步更新原理,我们简单回顾一下。当Vue中的Data被修改时,所有与该Data相关的Watcher都会被触发更新。首先,所有的观察者都会被添加到队列中。然后,调用nextTick方法执行异步任务。在异步任务的回调中,对Queue中的Watcher进行排序,然后进行相应的DOM更新。