美文网首页Vue
只需一个案例搞懂this.$nextTick()

只需一个案例搞懂this.$nextTick()

作者: microkof | 来源:发表于2019-09-21 11:00 被阅读0次

    前言

    this.$nextTick()并不是一个常用的API,但是到了关键的时候还就是必须用。但是它到底是做什么的,很多同学看了文档依然是一头雾水。先看文档:

    https://cn.vuejs.org/v2/api/#vm-nextTick

    将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。

    啥意思?下一次更新循环是啥?

    Vue的更新循环

    先了解一下Vue的异步更新队列

    最直白的说,Vue为了优化性能,使用了一个队列,Vue会先收集所有的数据更新需求,然后压到这个队列里。

    比如说你写了几条语句:

    this.a = 1
    this.b = 2
    

    表面上看,是更新了2个数据,那么视图会变化2次。

    但Vue不会让视图变化2次,因为视图变化的开销太大,Vue会在虚拟DOM中把2次变化全部实现,然后一次性渲染。2次数据变化都会压到队列里,Vue一直会等到队列全改完了,才会更新一次视图。这就是一次更新循环。

    那么下一次更新循环是啥?

    也是最直白的说,一批数据有修改->视图更新一次,这就是一次循环,下一次一批数据有修改->视图更新一次,就是下一次更新循环。

    这一次数据修改跟下一次数据修改的分界线是什么?简单但不严谨的说,就是这一波同步任务和下一波同步任务。这其中又涉及到了线程的概念,就是同步语句无论写多少句都是同一波任务,回调函数中的同步语句是另一波任务,有多个回调函数就有多少个下一波任务。

    这里面有个问题

    Vue有一个节省开销的机制,就是一个节点或组件没有渲染之前,对这个节点的任何修改都是无意义的,比如有一个组件,目前处于v-if=false状态,我想用ref获取这个组件,根本办不到。但问题来了,就是如果你把v-if=false改成true,你依然不能立即获取到。这跟Vue诞生之前的没有虚拟DOM的时代的常识是相悖的。

    早期时代,我们动态创建一个节点比如它id是a,只要它插入了DOM,那么可以立即使用document.getElementById('a')获取到它,但这是早期,在Vue里行不通。因为你的“v-if=false改成true”跟获取这个节点或者组件是2次更新循环。

    但是,我们写代码的时候,“v-if=false改成true”跟获取这个节点或者组件确实是强相关的操作,我总不能用setTimeout延时后一步操作吧?延时我也不知道延时多久哇?那么怎么办呢?

    this.$nextTick()就是干这个用的。

    案例一

    一个输入框,之前是v-if=false的,现在想显示,且获得焦点,怎么做?

          this.isShow = true
          this.$nextTick(() => {
            this.$refs['它的ref值'].focus()
          })
    

    这样就可以了,如果不写this.$nextTick(),会报错说无法给undefined执行.focus操作。

    同理,如果你打算获取元素的高度、宽度、位置之类的,也要用this.$nextTick()。

    注意:this.$nextTick()的回调函数应当使用箭头函数,以保证this指向vue实例。

    案例二

    官方针对mounted钩子函数有一句话:

    注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。

    可能之前你看了还是不懂它在说什么,现在就明白了,想对子组件操作,还是得加一个this.$nextTick():

    mounted() {
      this.$nextTick(() => {
        // 你想要对视图进行的修改操作
      })
    }
    

    没有回调函数会怎样

    官方说:

    如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不是原生支持 Promise (IE:你们都看我干嘛),你得自行 polyfill。

    也就是说,如果你在一个Promise链条中写代码写的正Happy,那么你就可以把

    this.$nextTick(() => {
        // 你想要对视图进行的修改操作
      })
    

    改成:

    this.$nextTick().then(() => {
        // 你想要对视图进行的修改操作
      })
    

    这样显得更Promise。

    相关文章

      网友评论

        本文标题:只需一个案例搞懂this.$nextTick()

        本文链接:https://www.haomeiwen.com/subject/srhyyctx.html