开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第25天,点击查看活动详情
$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的具体用法。
2.1 案例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.2 案例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
3.1 案例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>
效果如下:
3.2 案例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>
效果如下: