nextTick

 nextTick

 

<template>
    <view class="app">
        <view ref="msgview" style="color:red;">{{msg}}</view>
        <view v-if="msg1">Message got outside $nextTick: {{msg1}}</view>
        <view v-if="msg2">Message got inside $nextTick: {{msg2}}</view>
        <view v-if="msg3">Message got outside $nextTick: {{msg3}}</view>
        <button @click="changeMsg">
            Change the Message
        </button>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                msg: 'Hello Vue.',
                msg1: '1',
                msg2: '2',
                msg3: '3'
            };
        },
        methods: {
            changeMsg() {
                this.msg = "Hello world.";
                this.msg1 = this.$refs.msgDiv.innerHTML;
                console.log(this.$refs)
                this.$nextTick(() => {
                    this.msg2 = this.$refs.msgDiv.innerHTML;
                })
                this.msg3 = this.$refs.msgDiv.innerHTML;
            }
        },
    }
</script>

 

 图例

 Hello world.

 Message got outside $nextTick : Hello Vue

 Message got inside $nextTick Hello world

 Message got outside $nextTick: Hello Vue.

 

由上可知,msg1和msg3显示的内容还是变换之前的,而msg2显示的内容是变换之后的。

 

应用场景 :

    在vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,在created()钩子函数执行的时候DOM其实并未进行任何渲染, 而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js的代码放进Vue.nextTick()的回调函数中。与之对应的是mounted()钩子函数,因为该钩子函 数执行时所有的DOM挂载和渲染都已完成,此时在钩子函数中进行任何DOM操作都不会有问题。

    Vue异步执行DOM更新。只要观察到数据变化,Vue将开启一个队列,并缓冲在同一时间循环中发生的所有数据改变。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作上非常重要。然后,在下一个的事件循环“tick”中,Vue刷新队列并执行实际已去重的工作。

    Vue在内部尝试使用对异步队列使用原生的promise.then和MessageChannel,如果执行环境不支持,会采用setTimeout(fn,0)代替。

 

首先,先了解 nextTick 中定义的三个重要变量

  • callbacks

          用来存储所有需要执行的回调函数

  • pending

          用来标志是否正在执行回调函数

  • timerFunc

         用来触发执行回调函数

 

接下来,了解 nextTickHandler() 函数。 

function nextTickHandler () {
    pending = false
    const copies = callbacks.slice(0)
    callbacks.length = 0
    for (let i = 0; i < copies.length; i++) {
      copies[i]()
    }
  }

 

<div id=app>
  <div id="div" v-if="showDiv">我是显示文本</div>
  <button @click="showAndGetText">获取内容</button >
</div>
<script>
var app = new Vue({
  el: '#app',
  data () {
    return {
      showDiv : false
    },
    methods: {
      showAndGetText () {
        this.showDiv  = true
        let text = document.getElementById('div').innerHTML
        console.log(text)
      }
    }
  }
})
</script>

 

这段代码并不难理解,但是控制台回抛出一个 innerHTML of null 的错误,因为此时页面并未完成渲染,它并没有获取到div元素,这里涉及到一个Vue的重要概念:异步更新队列。

Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。在缓冲时会去除重复的数据,这样避免了不必要的计算和DOM操作。然后,在下一个时间循环Tick中,Vue刷新队列并执行已去重的工作。所以,如果你用一个for循环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘100次,这是我们不愿意看到的。

知道了Vue异步更新DOM的原理之后,上面的现象就不难理解了,事实上在下面语句执行时,,div仍然是没有被创建出来的,下面的

xthis.showDiv = true 

 

let text =document.getElementById('div').innerHTML console.log(text);

仍然读取的是这一次事件循环的DOM,而实际上在这一次事件循环中,DOM并没有更新,所以是读取不到的。我们需要等下一个Vue事件循环,DOM更新完成后再读取,就可以读取到了

posted on 2022-02-28 11:13  zhishiyv  阅读(134)  评论(0编辑  收藏  举报

导航