Vue.nextTick原理

使用

在双向绑定中,看如下代码:

vm.data1 = 'hello'
vm.$el.textContent	//此时并不会立即得到data1的值

这是因为vue并不会立即更新DOM,而Vue.nextTick方法可以实现在dom更新后所需要做的事情。

vm.data1 = 'hello'
Vue.nextTick(function(){
    console.log(vm.$el.textContent) // hello
})
原理

看了源码发现,nextTick实际就是创建一个新的宏任务(或微任务)。因为每个宏任务执行完后浏览器都需要重新渲染界面。当然,这不是主要的原因。

主要的原因是因为vue中双向绑定的数据在赋值后需要调用watcher的update方法(比如某个div绑定了一个数据,那么该div就会生成一个watcher,而update的方法就是更新自身的innerHTML了,当然这中间还有一层虚拟dom),而watcher的update也是通过nextTick来执行的。

也就是当vm.xx = 'xxx’的时候,watcher的update方法添加到了宏任务中,此时如果想要拿到更新后的dom,就可以再使用nextTick往末尾添加一个新任务,等前一个dom watcher执行完后就会轮到我们的nextTick,那个时候也就拿到了更新后的dom了。

nextTick使用宏任务还是微任务?
nextTick默认是使用的微任务,在看源码时有一段注释:

Here we have async deferring wrappers using both microtasks and (macro) tasks.
In < 2.4 we used microtasks everywhere, but there are some scenarios where
microtasks have too high a priority and fire in between supposedly
sequential events (e.g. #4521, #6690) or even between bubbling of the same
event (#6566). However, using (macro) tasks everywhere also has subtle problems
when state is changed right before repaint (e.g. #6813, out-in transitions).
Here we use microtask by default, but expose a way to force (macro) task when
needed (e.g. in event handlers attached by v-on).

翻译:

这里我们使用微任务和(宏)任务来实现异步延迟封装器。
在<2.4中,我们在任何地方都使用了微任务,但是在某些情况下,
微任务具有太高的优先级,并且在所谓的顺序事件(例如#4521,#6690)之间或甚至在同一事件的冒泡之间(#6566)之间发生冲突。
但是,在重新绘制之前状态更改时(例如#6813,out-in过渡),在任何地方使用(宏)任务也会产生细微的问题。
这里我们默认使用microtask,但在需要时(例如在v-on附加的事件处理程序中)提供强制(宏)任务的方法。

我在对data进行赋新值(如vm.xx = ‘xxx’)的调试过程中,发现nextTick使用的是宏任务的MessageChannel。

通过案例讲解流程
<div id="app">
    <div id="d1">{{ msg1 }}</div>
    <div id="d2">{{ obj.p1 }}</div>
    <button @click="mClick" value="btn">btn</button>
</div>
let vm = new Vue({
    el: '#app',
    data () {
        return {
            msg1: 'old',
            obj: {
                p1: 'old'
            }
        }
    },
    methods: {
        mClick () {
            this.msg1 = 'new'
            Vue.nextTick(function () {
                let d1 = document.getElementById("d1").innerHTML // new
                let d2 = document.getElementById("d2").innerHTML // new
            })
            this.obj.p1 = 'new'
        }
    }
})

在上方代码中,view是一个watcher,在this.msg1 = 'new'时就已经将该watcher加入了执行队列中,而该watcher的作用就是将view中的绑定内容替换成data的具体值。而在this.obj.p1 = 'new'时并不会重复添加watcher,会直接过滤掉。还有,在data里会生成两个observer对象,一个是data,一个是data.obj,因为该值是对象。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值