vue2.x之$nextTick

根据vue官方介绍,$nextTick的定义如下:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

可以简单地理解成vue会在渲染完DOM节点之后再执行$nextTick里面的回调函数。

案例介绍

看完上面的介绍,你可能还不能理解$next到底是什么?下面我们来看两个例子。

1. created阶段和beforeMounted阶段执行DOM

通过学习生命周期,我们知道在created阶段和beforeMounted阶段是没有DOM节点的,因此不能操作DOM节点

<template>
  <div class="hello">
    <div id="parent"></div>
  </div>
</template>

<script>
import mixin from '../mixin/data';
export default {
  name: 'HelloWorld',
  mixins: [mixin],
  data() {
    return {
      
    }
  },
  created() {
    let dom = document.querySelector('#parent');
    console.log('节点', dom);
  },
  methods: {
  }
}
</script>

效果如下: 获取到的节点为null

2. 修改数据之后立马操作新的DOM

这个案例其实也很常见,默认展示文本,点击编辑之后变成输入框。

<template>
  <div class="hello">
    <input type="text" v-model="txtVal" v-if="edit" ref='inputDom'/>
    <span v-else>{{txtVal}}</span>
    <button @click="saveFunc">{{edit ? '保存' : '编辑'}}</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      txtVal: '你好呀',
      edit: false
    }
  },
  methods: {
    saveFunc() {
      this.edit = !this.edit;
      this.$refs.inputDom.focus();
    }
  }
}
</script>

⏰ 效果如下

⏰ 原因分析:

vue官网中关于vue响应式原理的异步队列中有下列描述:

Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.thenMutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

简单地说就是当vue里面的数据发生改变,不会立马更新视图,而是先存储起来,如果一个数据被改了很多次,以最后一次修改的状态去更新视图。

通过上面的原因分析也可以看出来为什么可以使用setTimeout(fn, 0)代替$nextTick。

$nextTick的用法

1. 语法介绍

Vue.nextTick( [callback, context] ))

参数

  • {Function} [callback]
  • {Object} [context]

2. 案例中使用$nextTick

通过上面的两个案例我们来看一下this.$nextTick的具体用法。

⏰ 案例1

<template>
  <div class="hello">
    <div id="parent"></div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      
    }
  },
  created() {
    this.$nextTick(() => {
      let dom = document.querySelector('#parent');
      console.log('节点', dom);
    });
  },
  methods: {
  }
}
</script>

效果如下:

⏰ 案例2

<template>
  <div class="hello">
    <input type="text" v-model="txtVal" v-if="edit" ref='inputDom'/>
    <span v-else>{{txtVal}}</span>
    <button @click="saveFunc">{{edit ? '保存' : '编辑'}}</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      txtVal: '你好呀',
      edit: false
    }
  },
  methods: {
    saveFunc() {
      this.edit = !this.edit;
      this.$nextTick(() => {
        this.$refs.inputDom.focus();
      })
    }
  }
}
</script>

效果如下:

3. 定时器替代$nextTick

⏰ 案例1

<template>
  <div class="hello">
    <div id="parent"></div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      
    }
  },
  created() {
    setTimeout(() => {
      let dom = document.querySelector('#parent');
      console.log('节点', dom);
    });
  },
  methods: {
  }
}
</script>

效果如下:

⏰ 案例2

<template>
  <div class="hello">
    <input type="text" v-model="txtVal" v-if="edit" ref='inputDom'/>
    <span v-else>{{txtVal}}</span>
    <button @click="saveFunc">{{edit ? '保存' : '编辑'}}</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      txtVal: '你好呀',
      edit: false
    }
  },
  methods: {
    saveFunc() {
      this.edit = !this.edit;
      setTimeout(() => {
        this.$refs.inputDom.focus();
      })
    }
  }
}
</script>

效果如下:

posted on 2024-07-10 17:00  梁飞宇  阅读(43)  评论(0)    收藏  举报