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.then
、MutationObserver
和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>
效果如下: