Vue.nextTick浅析
Vue.nextTick浅析
Vue的特点之一就是响应式,但数据更新时,DOM并不会立即更新。当我们有一个业务场景,需要在DOM更新之后再执行一段代码时,可以借助nextTick
实现。以下是来自官方文档的介绍:
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。
具体的使用场景和底层代码实现在后面的段落说明和解释。
用途
Vue.nextTick( [callback, context] )
与 vm.$nextTick( [callback] )
前者是全局方法,可以显式指定执行上下文,而后者是实例方法,执行时自动绑定this
到当前实例上。
以下是一个nextTick
使用例子:
```<div id="app">
<button @click="add">add</button>
{{count}}
<ul ref="ul">
<li v-for="item in list">
{{item}}
</li>
</ul>
</div>
```
new Vue({
el: '#app',
data: {
count: 0,
list: []
},
methods:{
add() {
this.count += 1
this.list.push(1)
let li = this.$refs.ul.querySelectorAll('li')
li.forEach(item=>{
item.style.color = 'red';
})
}
}
})
以上的代码,期望在每次新增一个列表项时都使得列表项的字体是红色的,但实际上新增的列表项字体仍是黑色的。尽管data
已经更新,但新增的li元素并不立即插入到DOM中。如果希望在DOM更新后再更新样式,可以在nextTick
的回调中执行更新样式的操作。
new Vue({
el: '#app',
data: {
count: 0,
list: []
},
methods:{
add() {
this.count += 1
this.list.push(1)
this.$nextTick(()=>{
let li = this.$refs.ul.querySelectorAll('li')
li.forEach(item=>{
item.style.color = 'red';
})
})
}
}
})
解释
数据更新时,并不会立即更新DOM。如果在更新数据之后的代码执行另一段代码,有可能达不到预想效果。将视图更新后的操作放在nextTick
的回调中执行,其底层通过微任务的方式执行回调,可以保证DOM更新后才执行代码。
源码
在/src/core/instance/index.js
,执行方法renderMixin(Vue)
为Vue.prototype
添加了$nextTick
方法。实际在Vue.prototype.$nextTick
中,执行了nextTick(fn, this)
,这也是vm.$nextTick( [callback] )
自动绑定this
到执行上下文的原因。
nextTick
函数在/scr/core/util/next-tick.js
声明。在next-tick.js
内,使用数组callbacks
保存回调函数,pending
表示当前状态,使用函数flushCallbacks
来执行回调队列。在该方法内,先通过slice(0)
保存了回调队列的一个副本,通过设置callbacks.length = 0
清空回调队列,最后使用循环执行在副本里的所有函数。
const callbacks = []
let pending = false
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
接着定义函数marcoTimerFunc
、microTimerFunc
。
先判断是否支持setImmediate
,如果支持,使用setImmediate
执行回调队列;如果不支持,判断是否支持MessageChannel
,支持时,在port1
监听message
,将flushCallbacks
作为回调;如果仍不支持MessageChannel
,使用setTimeout(flushCallbacks, 0)
执行回调队列。不管使用哪种方式,macroTimerFunc
最终目的都是在一个宏任务里执行回调队列。
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
macroTimerFunc = () => {
setImmediate(flushCallbacks)
}
} else if (typeof MessageChannel !== 'undefined' && (
isNative(MessageChannel) ||
// PhantomJS
MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
const channel = new MessageChannel()
const port = channel.port2
channel.port1.onmessage = flushCallbacks
macroTimerFunc = () => {
port.postMessage(1)
}
} else {
/* istanbul ignore next */
macroTimerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
然后判断是否支持Promise
,支持时,新建一个状态为resolved
的Promise
对象,并在then
回调里执行回调队列,如此,便在一个微任务中执行回调,在IOS的UIWebViews组件中,尽管能创建一个微任务,但这个队列并不会执行,除非浏览器需要执行其他任务;所以使用setTimeout
添加一个不执行任何操作的回调,使得微任务队列被执行。如果不支持Promise
,使用降级方案,将microTimerFunc
指向macroTimerFunc
。
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
microTimerFunc = () => {
p.then(flushCallbacks)
// in problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer.
if (isIOS) setTimeout(noop)
}
} else {
// fallback to macro
microTimerFunc = macroTimerFunc
}
在函数nextTick
内,先将函数cb
使用箭头函数包装起来并添加到回调队列callbacks
。接着判断当前是否正在执行回调,如果不是,将pengding
设置为真。判断回调执行是宏任务还是微任务,分别通过marcoTimerFunc
、microTimerFunc
来触发回调队列。最后返回一个Promise
实例以支持链式调用。
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
if (useMacroTask) {
macroTimerFunc()
} else {
microTimerFunc()
}
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
而全局方法Vue.nextTick
在/src/core/global-api/index.js
中声明,是对函数nextTick
的引用,所以使用时可以显示指定执行上下文。
Vue.nextTick = nextTick
小结
本文关于nextTick
的使用场景和源码做了简单的介绍,如果想深入了解这部分的知识,可以去了解一下微任务mircotask
和宏任务marcotask
。
来源:https://segmentfault.com/a/1190000016495892
Vue.nextTick浅析的更多相关文章
- Vue中的nextTick()浅析
引言 在开发过程中,我们经常遇到这样的问题:我明明已经更新了数据,为什么当我获取某个节点的数据时,却还是更新前的数据? 一,浅析 为什么会这样呢?带着这个疑问先往下看. 先看一个小的例子: <d ...
- vue2.0 正确理解Vue.nextTick()的用途
什么是Vue.nextTick() 官方文档解释如下: 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 获取更新后的DOM,言外之意就是DOM更新 ...
- Vue nextTick 机制
背景 我们先来看一段Vue的执行代码: export default { data () { return { msg: 0 } }, mounted () { this.msg = 1 this.m ...
- vue nextTick使用
Vue nextTick使用 vue生命周期 原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue. ...
- Vue.$nextTick
`Vue.nextTick(callback)`,当数据发生变化,更新后执行回调. `Vue.$nextTick(callback)`,当dom发生变化,更新后执行的回调
- Vue 之 Vue.nextTick()
异步更新队列 可能你还没有注意到,Vue 异步执行 DOM 更新.只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变.如果同一个 watcher 被多次触发,只会一次 ...
- Vue.nextTick和Vue.$nextTick
`Vue.nextTick(callback)`,当数据发生变化,更新后执行回调. `Vue.$nextTick(callback)`,当dom发生变化,更新后执行的回调. 参考原文:http://w ...
- 我理解的关于Vue.nextTick()的正确使用
什么是Vue.nextTick() 官方文档解释如下: 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 我理解的官方文档的这句话的侧重点在最后那半 ...
- 全面解析Vue.nextTick实现原理
vue中有一个较为特殊的API,nextTick.根据官方文档的解释,它可以在DOM更新完毕之后执行一个回调,用法如下: // 修改数据 vm.msg = 'Hello' // DOM 还没有更新 V ...
随机推荐
- css-select的三角在不同浏览器的样式是不一样的,所以我们这样解决???
select{ width:57px; height:23px; border:1px solid #e9e9e9; outline: none; appearance: none; -moz-app ...
- python 线程模块
Python通过两个标准库thread和threading提供对线程的支持.thread提供了低级别的.原始的线程以及一个简单的锁. threading 模块提供的其他方法: threading.cu ...
- SPOJ D-query && HDU 3333 Turing Tree (线段树 && 区间不相同数个数or和 && 离线处理)
题意 : 给出一段n个数的序列,接下来给出m个询问,询问的内容SPOJ是(L, R)这个区间内不同的数的个数,HDU是不同数的和 分析 : 一个经典的问题,思路是将所有问询区间存起来,然后按右端点排序 ...
- 命令行创建 vue 项目(仅用于 Vue 2.x 版本)
1 .安装 Node.js 和 npm ( 验证安装成功输入下图 1 命令行可得 2:输入命令行 3 可得 4 即安装成功) 2.安装全局 webpack (安装依照下图输入命令行 1 耐心等待至到出 ...
- Maven开发环境搭建
配置Maven流程: 1.下载Maven,官网:http://maven.apache.org/ 2.安装到本地: 1 ).解压apache-maven-x.x.x-bin.zip文件 2 ).配置M ...
- [BZOJ1059]:[ZJOI2007]矩阵游戏(二分图匹配)
题目传送门 题目描述 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏.矩阵游戏在一个N×N黑白方阵进行(如同国际象棋一般,只是颜色是随意的).每次可以对该矩阵进行两种 ...
- for aws associate exam
Topics which I read based on the previous forum discussions Amazon DynamoDB January 2016 Day at the ...
- Linux shell - shift命令用法(转载)
位置参数可以用shift命令左移.比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1.$2.$3丢弃,$0不移动.不带参数的shift命令相当于shift 1. 非常 ...
- Java中String类中常用的方法
1.字符串与字符数组的转换 用toCharArray()方法将字符串变为字符数组 String str = "abcdef"; char c[] = str.tocharArray ...
- 【零售小程序】—— webview嵌套web端项目(原生开发支付功能)
index → index.wxml 套webwiew // url 活动url bindmessage 接收信息 <web-view src='{{url}}' bindmessage='m ...