vue面试题 $nextTick原理

什么是 $nextTick?

$nextTick 是一个用于在DOM更新完成后执行回调函数的方法

$nextTick解决的问题

平时在获取真实DOM的时候获取不到最新的DOM元素,使用$nextTick就可以

$nextTick 使用示例

<template>
  <div>
    <p ref="msgp">{{msg}}</p>
    <button @click="change">$nextTick</button>
  </div>
</template>

<script>
export default {
  name: 'nextTick',
  data() {
    return {
      msg: '未更新'
    }
  },
  methods: {
    change() {
      // 修改数据
      this.msg = '被更新了'
      // DOM还没有更新
      console.log(this.$refs.msgp.innerHTML)
      this.$nextTick(() => {
        // DOM更新了
        console.log('$nextTick:' + this.$refs.msgp.innerHTML)
      })
    }
  },
  created() {
  }
}
</script>

为什么获取不到最新的DOM元素?

因为Vue修改视图是异步执行的,这也是为了优化性能,在我们修改data中的数据时,Vue内部监听到依赖数据发生了改变,通过dep通知组件的watcher执行视图更新,每一次视图更新都需要重新生成vnode再进行新旧vnode比对,生成DOM元素挂载到页面上,这一轮操作是非常消耗性能的,所以Vue内部会把页面更新watcher推入到一个队列中,并加入了节流方法,当同步代码执行完了之后才会把队列中的watcher拿出来遍历更新视图,我们在使用this.$refs获取DOM的时候是同步代码,其实DOM还没有更新,所以是获取不到的。

为什么使用$nextTick就可以获取到最新DOM?

Vue的视图更新是异步执行的,使用的就是 nextTick,这也是能获取到最新DOM的原因,在Vue内部有一个nextTick函数,他也是使用队列去处理回调函数,并不是调用后就马上执行,首先是推入到一个队列中,当所有的同步代码执行完的时候再通过循环取出调用,接下来可以通过代码了解

let callback = []
let pending = false
let timerFunc

function flush() {
  callback.forEach((cb) => cb())
  pending = false
  callback = []
}

// 处理兼容问题
if (Promise) {
  timerFunc = () => {
    Promise.resolve().then(flush)
  }
} else if (MutationObserver) {
  let observe = new MutationObserver(flush)
  let textNode = document.createTextNode(1)
  observe.observe(textNode, {
    characterData: true,
  })
  timerFunc = () => {
    textNode.textContent = 2
  }
} else if (setImmediate) {
  // ie浏览器支持得定时器
  timerFunc = () => {
    setImmediate(flush)
  }
}

export function nextTick(cb) {
  callback.push(cb)
  // Promise.then
  if (!pending) {
    console.log('执行了')
    timerFunc() // 这个方法就是异步方法
    pending = true
  }
}
  1. 定义一个callback数组,每次调用nextTick就会把回调函数pushcallback数组中,因为callback.push是同步代码,timerFunc是异步代码,所以执行完所有push后才会调用timerFunc
  2. 那么视图更新和获取DOM的流程是怎么样的呢

通过伪代码来解释

data(){
    return {
        name: 'zs',
        list: [1,2,3]
    }
}

this.name = 'ls'
this.list.push(4)
const fn = () => {
  this.$el.innerHTML // 获取最新DOM
}
this.$nextTick(fn)

this.namethis.list修改了2个数据,会触发两次视图更新,这个时候就会把watcher推入到队列中,下面调用了$nextTick获取DOMnextTick中的fn函数也会推入到队列中,这个时候的数组是这样的[watcher,fn]通过循环调用,先执行视图更新,后获取DOM就可以获取到最新DOM

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值