浅谈Vue种的$nextTick机制

浅谈Vue种的$nextTick机制

nextTick出现的前提

因为在Vue种,其数据的变化和页面的渲染是异步的,即是我们在事件种修改了数据时,视图并不会立即更新,而是等在同一个事件循环(这里就涉及到了事件的循环机制)的所有数据都变化后,再执行视图的更新.

官方介绍

Vue.nextTick([callback,context])

  • 参数

    • {Function} [callback]
    • {Object} [contect]
  • 用法
    在下次DOM更新循环结束之后执行回调,在修改数据之后立即使用这个方法,获取更新后的DOM

// 修改数据
vm.msg = 'Hello'
// 当我们在这里调用DOM的数据时,它其实还没有更新
Vue.nextTick(function () {
    
    
    // DOM 更新了
})

// 2.1.0新增 Promise用法
Vue.nextTick()
    .then(function () {
    
    
    // 此时DOM已经更新
})

2.1.0 起新增:如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不原生支持 Promise (IE:你们都看我干嘛),你得自己提供 polyfill

DOM循环更新

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

在这里简单总结一下事件循环

同步代码执行=>查找异步队列,进入执行栈,执行Callback1[事件循环1]=>查找异步队列,进入执行栈,执行Callback[事件循环2]=>…

即每一次异步的Callback都会再独立形成一次事件循环

因此我们可以推出nextTick的触发机制

一次循环种的所有代码执行完毕 = > DOM更新 = > 触发nextTick的回调Callback = > 进入下一次循环

下面来个例子,帮助更好的理解nextTick机制

<template>
	<div class="app">
        <div ref="contentDiv">{
    
    {
    
    content}}</div>
        <div>在nextTick执行前获取内容:{
    
    {
    
    content1}}</div>
        <div>在nextTick执行之后获取内容:{
    
    {
    
    content2}}</div>
        <div>在nextTick执行前获取内容:{
    
    {
    
    content3}}</div>
    </div>
</template>

<script>
    export default {
    
    
        name:'App',
        data: {
    
    
            content: 'Before NextTick',
            content1: '',
            content2: '',
            content3: ''
        },
        methods: {
    
    
            changeContent () {
    
    
                this.content = 'After NextTick' // 在此处更新content的数据
                this.content1 = this.$refs.contentDiv.innerHTML //获取DOM中的数据
                this.$nextTick(() => {
    
    
                    // 在nextTick的回调中获取DOM中的数据
                    this.content2 = this.$refs.contentDiv.innerHTML 
                })
                this.content3 = this.$refs.contentDiv.innerHTML
            }
        },
        mount () {
    
    
            this.changeContent()
        }
    }
</script>

打开网页后,我们可以发现结果为:

After NextTick

在nextTick执行前获取内容:Before NextTick

在nextTick执行之后获取内容:After NextTick

在nextTick执行前获取内容:Before NextTick

总结:
首先是在changeContent方法中对content进行了更改
虽然content1和content3获取内容都是再content数据更改之后的,但是它们属于同一个事件循环,因此content1和content3获取的还是’ Before NextTick ', 而content2获取内容的语句写在nextTick的回调中,在DOM更新之后执行,所以能够获取到更新后的 ’ After NextTick

应用场景

在created生命周期执行DOM操作

当在create()生命周期函数中执行DOM操作是不可取的,因为此时的DOM并未进行任何的渲染.所以解决方法是将DOM操作写进Vue.nextTick()的回调函数中,或者是将操作放入mounted()钩子函数中

在数据变化后需要进行基于DOM结构的操作

在我们更新数据之后,如果还有操作要根据更新后的DOM节后进行操作,那么我们就应当将这部分操作放入 Vue.nextTick() 回调函数中

可能你还没有注意到,Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。
例如,当你设置 vm.someData = ‘new value’ ,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用

总结
  • 在同一事件循环中,当所有的同步数据更新执行完毕后,才会调用nextTick
  • 在同步执行环境中的数据完全更新完毕之后,DOM才会开始渲染
  • 在同一个事件循环中,若出现多个nextTick,将会按最初的执行顺序进行调用
  • 每个异步回调函数执行后都会存在一个独立的事件循环中,对应自己独立的nextTick

おすすめ

転載: blog.csdn.net/weixin_46872121/article/details/112424489