Vue中的nextTick()浅析
引言
在开发过程中,我们经常遇到这样的问题:我明明已经更新了数据,为什么当我获取某个节点的数据时,却还是更新前的数据?
一,浅析
为什么会这样呢?带着这个疑问先往下看。
先看一个小的例子:
<div class="app">
<div ref="msgDiv">{{msg}}</div>
<div v-if="msg1">Message got outside $nextTick: {{msg1}}</div>
<div v-if="msg2">Message got inside $nextTick: {{msg2}}</div>
<div v-if="msg3">Message got outside $nextTick: {{msg3}}</div>
<button @click="changeMsg"> Change the Message </button>
</div>
new Vue({
el: '.app',
data: { msg: 'Hello Vue.', msg1: '', msg2: '', msg3: '' },
methods: {
changeMsg() {
this.msg = "Hello world."
this.msg1 = this.$refs.msgDiv.innerHTML
this.$nextTick(() => {
this.msg2 = this.$refs.msgDiv.innerHTML
})
this.msg3 = this.$refs.msgDiv.innerHTML
}
}
})
点击按钮前
点击按钮后
我们发现当我们更新了msg的值之后,msg1和msg3获取的还是原来的msg的值。而msg2使用了 this.$nextTick()方法之后 值就可以正常更新了。
那么这里的 this.$nextTick起到了什么作用呢?
因为虽然此时this.msg已经变了 但是dom节点的值没有更新,也就是说,变的只是数据,而视图节点的值未更新。所以当这时去获取节点的值this.msg时,拿到的还是原来的数据。那问题来了,我啥时候才能拿到更新的数据呢?????????往下看!
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then 和MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0)代替。
上面这段话是Vue的官方文档中详细解释,大概意思是说:我(Vue)看到数据变化了,我不会立马直接更新试图,而是先建立一个队列,把跟这个数据有关的所有dom节点放进去,直到在下一个的事件循环“tick”中,我(Vue)才 刷新队列并执行实际 (已去重的) 工作。重点来了,当所有dom 更新后,会触发nextTick回调函数。也就是说dom更新之后 nextTick()被调用了!这也就解释的通了 为啥有且只有msg2发生了变化!
简单的说,nextTick()的作用是:将回调延迟到DOM 更新循环之后执行。在修改数据时立即使用它,然后等待 DOM 更新,便可达到获取 更新后的节点 的目的。
相信读到这里你应该有了自己的理解,接下来说说nextTick()的应用场景和源码分析。
二、应用场景
下面了解下nextTick的主要应用的场景及原因。
1. 在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。
2. 在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted()钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
3. 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。
例如,当你设置vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
三、nextTick源码浅析
作用
Vue.nextTick用于延迟执行一段代码,它接受2个参数(回调函数和执行回调函数的上下文环境),如果没有提供回调函数,那么将返回promise对象。
源码
export const nextTick = (
function () {
const callbacks = []
let pending = false
let timerFunc
function nextTickHandler () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
首先,先了解nextTick中定义的三个重要变量。
callbacks
用来存储所有需要执行的回调函数
pending
用来标志是否正在执行回调函数
timerFunc
用来触发执行回调函数
接下来,了解nextTickHandler()函数。
这个函数用来执行callbacks里存储的所有回调函数。
接下来是将触发方式赋值给timerFunc。
先判断是否原生支持promise,如果支持,则利用promise来触发执行回调函数;
否则,如果支持MutationObserver,则实例化一个观察者对象,观察文本节点发生变化时,触发执行所有回调函数。
如果都不支持,则利用setTimeout设置延时为0。
最后是queueNextTick函数。因为nextTick是一个即时函数,所以queueNextTick函数是返回的函数,接受用户传入的参数,用来往callbacks里存入回调函数。
上图是整个执行流程,关键在于timeFunc(),该函数起到延迟执行的作用。
从上面的介绍,可以得知timeFunc()一共有三种实现方式。
- Promise
- MutationObserver
- setTimeout
其中Promise和setTimeout很好理解,是一个异步任务,会在同步任务以及更新DOM的异步任务之后回调具体函数。
下面着重介绍一下MutationObserver。
MutationObserver 是HTML5中的新API,是个用来监视DOM变动的接口。他能监听一个DOM对象上发生的子节点删除、属性修改、文本内容修改等等。
调用过程很简单,但是有点不太寻常:你需要先给他绑回调:
let mo = new MutationObserver (callback);
通过给 MutationObserver 的构造函数传入一个回调,能得到一个 MutationObserver实例,这个回调就会在 MutationObserver 实例监听到变动时触发。
这个时候你只是给 MutationObserver 实例绑定好了回调,他具体监听哪个DOM、监听节点删除还是监听属性修改,还没有设置。而调用他的observer方法就可以完成这一步:
var domTarget = 你想要监听的dom节点mo.observe (domTarget, { characterData:true//说明监听文本内容的修改。})
在nextTick中 MutationObserver 的作用就如上图所示。在监听到DOM更新后,调用回调函数。
其实使用 MutationObserver 的原因就是 nextTick 想要一个异步API,用来在当前的同步代码执行完毕后,执行我想执行的异步回调,包括 Promise 和 setTimeout 都是基于这个原因。
参考博客:https://www.jianshu.com/p/a7550c0e164f https://segmentfault.com/a/1190000016495892
Vue中的nextTick()浅析的更多相关文章
- 【转】简单理解Vue中的nextTick
前言: Vue中的nextTick涉及到Vue中DOM的异步更新,感觉很有意思,特意了解了一下.其中关于nextTick的源码涉及到不少知识,很多不太理解,暂且根据自己的一些感悟介绍下nextTick ...
- 简单理解Vue中的nextTick
Vue中的nextTick涉及到Vue中DOM的异步更新,感觉很有意思,特意了解了一下.其中关于nextTick的源码涉及到不少知识,很多不太理解,暂且根据自己的一些感悟介绍下nextTick. 一. ...
- 详解Vue中的nextTick
Vue中的nextTick涉及到Vue中DOM的异步更新,感觉很有意思,特意了解了一下.其中关于nextTick的源码涉及到不少知识,很多不太理解,暂且根据自己的一些感悟介绍下nextTick. 一. ...
- Vue中之nextTick函数源码分析
Vue中之nextTick函数源码分析 1. 什么是Vue.nextTick()?官方文档解释如下:在下次DOM更新循环结束之后执行的延迟回调.在修改数据之后立即使用这个方法,获取更新后的DOM. 2 ...
- 前端学习笔记系列一:3 Vue中的nextTick
一.示例 先来一个示例了解下关于Vue中的DOM更新以及nextTick的作用. 模板 <div class="app"> <div ref="msgD ...
- vue中的nextTick
今天在浏览elementUI官网时,又一次看到了nextTick,其实nextTIck我已经不是第一次看到了,但之前都没怎么弄懂,这次决定好好研究一番-- 异步说明 vue是异步执行DOM更新的. ...
- 聊聊VUE中的nextTick
在谈nextTick之前,先要说明一件事,可能在我们平时使用vue时并没有关注到,事实上,vue执行的DOM更新是异步的. 举个栗子: <template> <div class=& ...
- 关于vue中的nextTick深入理解
定义[nextTick.事件循环] nextTick的由来: 由于VUE的数据驱动视图更新,是异步的,即修改数据的当下,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新 ...
- vue中的$nextTick的常用思路
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新. $nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextT ...
随机推荐
- 模板——BigInteger
#include <iostream> #include <cstring> #include <string> #include <vector> # ...
- P1073 奇数还是偶数
题目描述 给你 \(N(1 \le N \le 1000)\) 个数,你需要判断每个数是奇数还是偶数. 输入格式 输入的第一行包含一个整数 \(N(1 \le N \le 1000)\) . 接下来 ...
- H3C 路由的来源
- 如何用python“优雅的”调用有道翻译?
前言 其实在以前就盯上有道翻译了的,但是由于时间问题一直没有研究(我的骚操作还在后面,记得关注),本文主要讲解如何用python调用有道翻译,讲解这个爬虫与有道翻译的js“斗争”的过程! 当然,本文仅 ...
- opacity兼容性以及存在问题处理
opacity兼容性以及存在问题处理 opacity兼容性 opacity属性是CSS3的属性,用于设置元素的不透明级别.语法: opacity: value | inherit; ①值value表示 ...
- BZOJ3527 推出卷积公式FFT求值
BZOJ3527 推出卷积公式FFT求值 传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3527 题意: \(F_{j}=\sum_{i&l ...
- P3810 陌上花开 CDQ分治
陌上花开 CDQ分治 传送门:https://www.luogu.org/problemnew/show/P3810 题意: \[ 有n 个元素,第 i 个元素有 a_i. b_i. c_i 三个属性 ...
- UI 素材相关收集
图标素材收藏网址 阿里:iconfont.cn 图片压缩:https://tinypng.com/ gif动图压缩:https://tool.gifhome.com
- LeetCode3_无重复字符的最长子串(数组&字符串问题)
题目: 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "ab ...
- 专业版12.0试用,打开演示账套,提示“列名FPlatver无效”
你好,我代表研发来说明一下这个问题:一.产生的原因:由于KIS产品今年陆续都增加了应用平台,前期平台兼容性还有些问题,如果一台电脑有多个不同版本的平台,就会产生冲突,因此报错.1.可能您的机器装了多个 ...