Vue nextTick彻底理解

前言

含义和使用


nextTick的官方解释:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。


啥意思呢,即我们对Vue中data数据的修改会导致界面对应的响应变化,而通过nextTick方法,可以在传入nextTick的回调函数中获取到变化后的DOM,讲起来可能还是有点梦幻,下面我们直接使用nextTick体验一下效果。


比如我们有如下代码:

<template>
   <div>
      <button @click='update'>更新数据</button>
      <span id='content'>{
  {message}}</span>
   </div>
</template>
<script>
  export default{
    
      data:{
    
          message:'hello world'
      },
      methods:{
    
          update(){
    
              this.message='Hello World'
              console.log(document.getElementById('content').textContent);
              this.$nextTick(()=>{
    
                  console.log(document.getElementById('content').textContent);
              })
          }
      }
  }
</script>


上述代码第一次输出结果为hello world,第二次结果为更新后的Hello World

hello world
Hello World


即我们在update方法中第一行对message的更新,并不是马上同步到span中,而是在完成span的更新之后回调了我们传入nextTick的函数。

// 修改数据
vm.data = 'Hello'
//---> DOM 还没有更新

Vue.nextTick(function () {
   
  //---> DOM 更新了
})


这里我们也可以理解为Vue中数据的更新不会同步触发dom元素的更新,也就是说dom更新是异步执行的,并且在更新之后调用了我们传入nextTick的函数。


那么问题来了,Vue为什么需要nextTick呢?nextTick又是如何实现的呢

探索


这里我们就抱着好奇的心态,理解一下nextTick函数的实现原理,加深对Vue底层原理的理解。


要想理解nextTick的设计意图和实现原理我们需要两块的前置知识理解:

  1. Vue响应式原理(理解设计意图)
  2. 浏览器事件循环机制(理解原理)


因此本次行文先简单讲解以上两部分内容,最后将知识整合详细介绍nextTick的实现原理。

响应式原理

这部分内容主要介绍Vue的响应式实现原理,已经理解的同学可以跳过。

Vue响应原理的核心是数据劫持和依赖收集,主要是利用Object.defineProperty()实现对数据存取操作的拦截,我们把这个实现称为数据代理;同时我们通过对数据get方法的拦截,可以获取到对数据的依赖,并将出所有的依赖收集到一个集合中。

 Object.defineProperty(data, key, {
   
    enumerable: true,
    configurable: true,
    //拦截get,当我们访问data.key时会被这个方法拦截到
    get: function reactiveGetter () {
   
        //我们在这里收集依赖
        return data[key];
    },
    //拦截set,当我们为data.key赋值时会被这个方法拦截到
    set: function reactiveSetter (newVal) {
   
        //当数据变更时,通知依赖项变更UI
    } 
})


下面为了更好的理解之后nextTick的实现原理,我们需要先实现一个简化版的Vue。

Vue类


首先我们实现一个Vue类,用于创建Vue对象,它的的构造方法接收一个options参数,用于初始化Vue。

class Vue{
   
    constructor(options){
   
       this.$el=options.el;
       this._data=options.data;
       this.$data=this._data;
       //对data进行响应式处理
       new Observe(this._data);
   }
}
//创建Vue对象
new Vue({
   
    el:'#app',
    data:{
   
      message:'hello world'
    }
})


上面的代码中我们首先创建了一个Vue的类,构造函数跟我们平时使用的Vue大致一致,为了容易理解我们这里只处理了参数eldata
我们发现构造函数的最后一行创建了一个Observe类的对象,并传入data作为参数,这里的Observe就是对data数据进行响应式处理的类,接下来我们看一下Observe类的简单实现。

Observe类


我们在Observe类中实现对data的监听,就是通过Object.defineProperty()方法实现的数据劫持,代码如下。

class Observe{
   
    constructor(data){
   
       //如果传入的数据是object
       if(typeof data=='object'){
   
           this.walk(data);
       }
    }
    //这个方法遍历对象中的属性,并依次对其进行响应式处理
    walk(obj){
   
        //获取所有属性
        const keys=Object.keys(obj);
        for (let i = 0; i < keys.length; i++) {
   
            //对所有属性进行监听(数据劫持)
            this.defineReactive(obj, keys[i])
        }
    }
    defineReactive(obj,key){
   
        if(typeof obj[key]=='object'){
   
            //如果属性是对象,那么那么递归调用walk方法
            this.walk(obj[key]);
        }
        const dep=new Dep();//Dep类用于收集依赖
        const val=obj[key];
        Object.defineProperty(obj, key, {
   
            enumerable: true,
            configurable: true,
            //get代理将Dep.target即Watcher对象添加到依赖集合中
            get() {
   
              //这里在创建Watcher对象时会给Dep.target赋值
              if (Dep.target
  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一拳小和尚LXY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值