# $nextTick相关

  • 1 数据发生变化,触发 setter
  • 2 dep.notice()触发 dep 下的 weather
  • 3 weatcher.update(),update 有同步更新,和异步更新(queueWeather)。
  • 4 将一个观察者对象 weather,push 进 queue 队列,在 queue 队列中已经存在相同的 id 则该观察者对象 weather 将被跳过.利用 hash 的 map 结构 对 weather 去重,防止相同的 weather 被重复添加到 queue 中。
  • 6 nextTick 利用事件循环,在一下次 tick 时,flushSchedulerQueue 执行所有 queue 队列中的 weather,更新视图。

# 什么时候会使用到nextTick?

vue在 beforeCreate,created阶段主要完成初始化的操作,但如果我们在初始化的阶段来获取DOM此时DOM 其实并未进行任何渲染,所以一定要把DOM相关的操作放在nextTick中。

如果是mounted阶段,vue已经挂载和渲染完成,所以此时获取DOM是可以拿到的。

<template>
//打印的结果是 begin,为什么我们明明已经将 test 设置成了“end”,获取真实 DOM 节点的 innerText 却没有得到我们预期中的“end”,可见该操作是异步的。
  <div>
    <div ref="test">{{test}}</div>
    <button @click="handleClick">tet</button>
  </div>
</template>
export default {
    data () {
        return {
            test: 'begin'
        };
    },
    methods () {
        handleClick () {
            this.test = 'end';
            console.log(this.$refs.test.innerText);//打印“begin”
        }
    }
}

通过 nextTick,我们可以拿到 DOM 操作之后的结果。

<template>
 <div class="" id="app">
  <span ref="msg">{{msg}}</span>
     <button @click="change">button</button>
  </div>
</template>

<script>
export default {
 name: 'app',
 data(){
  return{
   msg:'1111'
  }
 },
 methods:{
  change(){

            this.msg = "222"
            console.log('1111',this.$refs.msg.innerHTML) //打印结果 111
   this.$nextTick(()=>{
    console.log('1111',this.$refs.msg.innerHTML) //打印结果 222
   })
  },
 }
}
</script>

# 数据变化时,weather 如何更新?

  • 1 数据发生变化,触发 setter
  • 2 dep.notice()触发 dep 下的 weather
  • 3 weatcher.update(),update 有同步更新,和异步更新(queueWeather)。
update () {
    /* istanbul ignore else */
    if (this.lazy) {
        this.dirty = true
    } else if (this.sync) {
        /*同步则执行run直接渲染视图*/
        this.run()
    } else {
        /*异步推送到观察者队列中,下一个tick时调用。*/
        queueWatcher(this)
    }
}

# queueWeather 对 weather 去重?

  • 1 将一个观察者对象 weather,push 进 queue 队列,在 queue 队列中已经存在相同的 id 则该观察者对象 weather 将被跳过.
  • 2 利用 hash 的 map 结构 对 weather 去重,防止相同的 weather 被重复添加到 queue 中。
  • 3 nextTick 利用事件循环,在一下次 tick 时,执行所有 queue 队列中的 weather,更新视图。
let has = {};
let queue = [];
let waiting = false;

function queueWatcher(watcher) {
    //获取weather的id
    const id = watcher.id;
    if (has[id] == null) {
        has[id] = true; // {'1':true,'2':true}
        queue.push(watcher);

        if (!waiting) {
            waiting = true;
            nextTick(flushSchedulerQueue);
        }
    }
}

## 

function flushSchedulerQueue () {
    let watcher, id;

    for (index = 0; index < queue.length; index++) {
        watcher = queue[index];
        id = watcher.id;
        has[id] = null;
        watcher.run();
    }

    waiting  = false;
}

# 为什么会使用 nextTick?

vue 中我们修改 data 中的 key,并不可能每一次修改 data 中的 key 都要渲染一下页面。在 vue 中会把多次对 data 中 key 的修改合并,放在一个事件循环中,然后利用事件循环机制,当前这个事件循环对 key 的所有修改都结束会调用一个 nextTick 函数,标志此时渲染结束。

事件循环的事件执行完,先通过 Promise 实现 nextTick 如果 nextTick 不支持使用 MucationObserver 如果 MucationObserver 不支持使用 setTimeOut。

修改数据时,视图并不会即时的更新,而是等在同一事件循环的所有数据变化完成后,再进行视图更新。类似于 Event Loop 事件循环机制。

Vue 实现响应式并不是在数据改变后就立即更新 DOM,而是在一次事件循环的所有数据变化后再异步执行 DOM 更新.

nextTick

# 为什么我们频繁修改 data 属性,不会多次更新 view?

当我们同时修改多次 data 属性时候,该判断 if (has[id] == null) 防止重复添加更新任务,并且利用了 event loop 机制在合适的时机去更新视图。

在 update 方法中,实际调用 nextTick 更新视图。 在事件循环中,当前宏任务调用栈清空才会去执行微任务。