vue异步队列

278 阅读1分钟
<div id="example">
  <div>{{ words }}</div>
  <input type="button" @click="clickHanler" value="click"/>
</div>

  var vm = new Vue({
    el:"#example",
    data: {
      name: "Devin",
      greetings: "Hello"
    },
    computed: {
      words: function(){
        return this.greetings + ' ' + this.name + '!'
      }
    },
    methods: {
      clickHanler(){
        this.name = 'Devinn';
        this.name = 'Devinnzhang';
        this.greetings = 'Morning';
      }
    }
  });

此时 Vue 中创建了两个 watcher,一个是渲染 watcher,负责渲染模板,一个是 computed watcher 负责计算属性。当点击 clickHandler 的时候数据发生变化会通知道两个 watcher ,watcher进行更新。这里的 watcher 更新会有两个问题:

1、渲染watcher 和 computed watcher 同时订阅了变化的数据,哪一个先执行,执行顺序是怎么样的?

2、在一个事件循环中 name 的变化触发了两次,greetings 触发了一次,对应两个 watcher 一共执行了几次?DOM渲染了几次?

原理:

当数据发生变化时,会调用 dep.notity() 进而通知订阅的 watcher 进行 更新

  Watcher.prototype.update = function update () {
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true;
    } else if (this.sync) {
      this.run();
    } else {
      queueWatcher(this);
    }
  };

可以看到 update 中 watcher 并没有立即执行( 同步的除外),而是调用了 queueWatcher (将更新的 watcher 加入到了一个队列中),看下 queueWatcher 的实现:

  function queueWatcher (watcher) {
   var id = watcher.id;
   console.log('watcherId='+ id + 'exporession=' + watcher.expression);
   if (has[id] == null) {
     //console.log('watcherId='+ id + 'exporession=' + watcher.expression);
     has[id] = true;
     if (!flushing) {
       queue.push(watcher);
     } else { 
       // if already flushing, splice the watcher based on its id
       // if already past its id, it will be run next immediately.
       var i = queue.length - 1;
       while (i > index && queue[i].id > watcher.id) {
         i--;
       }
       queue.splice(i + 1, 0, watcher);
     }
     // queue the flush
     if (!waiting) {
       waiting = true;

       if (!config.async) {
         flushSchedulerQueue();
         return
       }
       nextTick(flushSchedulerQueue);
     }
   }
 }

这里的 queueWatcher 做了两个事:

1、将 watcher 压入队列,重复的 watcher 知会被压入一次,这样在一个事件循环中 触发了多次的 watcher 只会被压入队列一次。如例子中异步队列中只有一个 渲染 watcher 和一个computed watcher;

2、调用 nextTick(flushSchedulerQueue) 对队列中的 watcher 进行异步执行, nextTick 实现异步,flushSchedulerQueue 对 watcher 进行遍历执行。