Vue 异步更新队列$nextTick

前面的话

这篇文章将介绍关于Vue异步更新队列的知识,学习异步更新队列将帮助我们更好的理解Vue的更新机制。

先来看一个例子:一个div,有v-if控制是否渲染,点击按钮对div的显示/隐藏做改变,并在显示时,获取里边的文本 。

    <div id="app1">
        <div id="div" v-if="showDiv">这是一段文本</div>
        <button @click="getText">获取文本</button>
    </div>  
    <script>
			var app1 = new Vue({
        el: '#app1',
        data: {
            showDiv: false 
        },
        methods: {
            getText() {
                this.showDiv = true;
                var text = document.getElementById("div").innerHTML;
                console.log(text);
            }
        }
    })
	</script>
    

按照平时思路:

当v-if="true"时,div被渲染出来,document.getElementById(“div”).innerHTML可以获取里面的文本。

实际情况:

代码运行后,控制台会抛出一个错误,意思是获取不到div元素。这里就涉及到Vue的一个重要概念:异步更新队列。
在这里插入图片描述

JavaScript的运行机制

为了方便理解Vue.js异步更新策略和nextTick,先介绍以下JS的运行机制(小柒在前面更新过关于JavaScript的运行机制的文章详解),参考阮一峰老师的JavaScript 运行机制详解:再谈Event Loop。JS是单线程的,意思就是同一时间只能做一件事情。它是基于事件轮询的,具体可以分为以下几个步骤:

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

Vue异步更新队列

Vue在观察数据变化时并不是直接更新DOM,而是开启一个队列,并缓存同一个事件循环中发生的所有数据改变。在缓存时会除去重复数据,从而避免不必要的计算和DOM操作。然后,在下一个事件循环tick中,Vue刷新队列并执行实际(已去重)的工作。

[去重机制]

Vue这种去重机制减少了开销,如果一个for循环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘 100 次。

[异步更新队列实现的选择]

Vue会根据当前浏览器环境优先使用原生的Promise.then 和MutationObserve。
如果都不支持就会采用setTimeout代替。

[代码报错原因]

在执行this.showDiv = "true"时,div并没有被创建出来,直到下一个Vue事件循环时,才开始创建。

使用异步更新队列

Vue提供了$ nextTick来告知DOM声明时候完成更新,我们可以在$nextTick中进行div内容的获取,修改上面的例子。

<div id="app2">
        <div id="div" v-if="showDiv">这是一段文本</div>
        <button @click="getText">获取文本</button>
 </div>
<script>
	 var app2 = new Vue({
        el: '#app2',
        data: {
            showDiv: false 
        },
        methods: {
            getText() {
                this.showDiv = true;
                // 通知DOM更新完成
                this.$nextTick(function() {
                    var text = document.getElementById("div").innerHTML;
                    console.log(text);
                })
               
            }
        }
    })
</script>

通过修改后首次点击不会报错

再看一个例子:

<div id="app3">
        <div id="div" >{{number}}</div>
        <button @click="handleClick">click</button>
    </div>
<script>
	var app3 = new Vue({
        el:'#app3',
        data:{
            number: 0
        },
        methods: {
            // 只会应用最后一次改变 
            handleClick() {
                for(let i = 0; i < 100; i++){
                    this.number++;
                }
                console.log(this.number);
            }
        }
    }) 
</script>

在这里插入图片描述
视图上每一次响应党的是最后一次数据的改变,而不是从0-100一个一个变化。

这个例子更好的体验了Vue中异步更新队列的去重机制。

$nextTick的用途

应用场景: 在视图更新之后,基于新的视图进行操作。

看一个例子: 点击show按钮使得 原来v-if="false"的input框显示,并且获得焦点

  <div id="app4">
        <input type="text" ref="input" v-if="showInput">
        <button @click="handleClick">show</button>
    </div>
  <script>
    var app4 = new Vue({
        el: '#app4',
        data:{
            showInput: false
        },
        methods: {
            handleClick() {
                this.showInput = true;
                this.$nextTick(function() {
                    this.$refs.input.focus();
                });
            }
        }
    })
</script>
   
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值