vue中$nextTick详细讲解保证你一看就明白

  • A+
所属分类:Web前端

1.功能描述

今天我们要实现这个一个小功能; 页面渲染完成后展示一个div元素; 当点击这个div元素后; div元素消失; 出现一个input元素;并且input元素聚焦 想必大家我觉得简单,我们一起来看看~  创建一个组件,组件名称NextTick.vue; 在页面中引入注册 

2.父组件

<template>   <div>     <next-tick></next-tick>   </div> </template>  <script lang="ts"> import NextTick from "../components/NextTick.vue" export default {   name:"About",   components:{     NextTick   }, } </script> 

3.子组件NextTick.vue

<template>     <div>         <div>我是组件</div>         <div v-if="flag" class="sun" @click="handerClick">显示input</div>         <input v-else ref="inputRef" class="yuelaing"/>     </div> </template> <script> export default {     data(){         return{             flag:true,         }     },     methods: {         handerClick(){             this.flag=false;             this.$refs.inputRef.focus();         },     }, } </script> 

vue中$nextTick详细讲解保证你一看就明白

4为什么是undefined

this.flag=false; this.$refs.inputRef.focus(); 当执行页面操作的时候,this.$refs.inputRef.focus(); 是需要消耗时间的(还没有还得及刷新;还是旧的页面) 此时还没有获取到dom元素。 所以会报错。  解决方式1: 因此只要让页面能够获取元素就行;使用setTimeout setTimeout(()=>{       this.$refs.inputRef.focus(); },100) 这样来处理这个问题,是可以的; 但是显得非常的不专业;  解决方式2: //当组件根据最新的data数据,重新在视图上完成渲染后,在执行里面的函调函数 this.$nextTick(()=>{     this.$refs.inputRef.focus(); }) 

5.将v-if更改为v-show可以获取焦点吗?

有人说:因为v-if是动态创建和销毁; 在创建和销毁的过程中,是需要时间的! 所以才会使用v-if获取不到元素节点 用v-show就可以避免。 感觉说的有点道理? 我们尝试一下将v-if换成v-show 
<template>     <div>         <div>我是组件</div>         <div v-show="flag" class="sun" @click="handerClick">显示input</div>         <input v-show="!flag" ref="inputRef" class="yuelaing"/>     </div> </template> <script> export default {     data(){         return{             flag:true,         }     },     methods: {         handerClick(){             this.flag=false;             console.log( this.$refs.inputRef);             this.$refs.inputRef.focus();         },     }, } </script> 

vue中$nextTick详细讲解保证你一看就明白

6.实际结果

我们发现虽然是页面没有报错,但是还没有聚焦; 改为v-show明显也不能够解决这个问题  之所以会出现这个问题 是因为子组件中将this.flag=false后, 立刻去执行了下面的代码 this.$refs.inputRef.focus(); 而在执行的时候,视图还没没有来得及刷新; 还是旧的页面,此时还不能够获取到dom元素 因此出现了undefined; 也就是为什么我们加上延时后就可以聚焦了;  当组件根据最新的data数据, 重新在视图上完成渲染后,在执行里面的函调函数 这就是$nextTick的基本用法 this.$nextTick(()=>{     this.$refs.inputRef.focus(); }) 

7.将组件变成页面可以获取焦点吗?

又有人说:因为是子组件,子组件比父组件后渲染。 所以没有获取到元素节点。 这也是理由.... 感觉还没有上一个小伙伴说的对 为了解决疑惑。我们决定将子组件变成页面在看看 
<template>   <div>     <div>我是组件</div>     <div v-show="flag" class="sun" @click="handerClick">显示input</div>     <input v-show="!flag" ref="inputRef" class="yuelaing"/>   </div> </template> <script> export default {   data(){     return{         flag:true,     }   },   methods: {       handerClick(){         this.flag=false;         this.$refs.inputRef.focus();       },   }, } </script> 
我们发现仍然不可以; 这就充分说明了: 更新data的数据后,vue并不是实时更新的。 数据更新到显示到页面有时间差, 我们在时间差内调用页面数据,当然获取不到。 也就是说:Vue在更新 DOM 时是异步执行的 

8.为什么会有$nextTick

之所以会有$nextTick; 因为在vue中数据发生变化后; 视图上的dom并不会立刻去跟新; dom的跟新是需要时间的 下面我们通过一个小实验来看一下 
<template>   <div>     <div ref="unique">       <h1>{{ cont }}</h1>     </div>     <div  class="sun" @click="handerClick">改变值</div>   </div> </template> <script> export default {   data(){     return{       cont:'我是默认值'     }   },   methods: {       handerClick(){         this.cont='我改变了默认值';         console.log('1==>',this.$refs.unique.innerText);         this.$nextTick(()=>{           console.log('2==>',this.$refs.unique.innerText);         })       },   }, } </script> 

vue中$nextTick详细讲解保证你一看就明白

我们发现,第一次的值和第二次的值,是不一样的; 因为视图上dom的跟新是需要之间的; 我们在这个之间差内去获取元素值; 仍然是旧值;所以第一次的值是最初的值; 第二次的值才是改变后的值; 由于我们希望跟新数据后,仍然可以立刻获取dom上的值 所以vue提供了$nextTick就可以解决这个问题 

9.Vue.nextTick和this.$nextTick差别

Vue.nextTick是全局方法 this.$nextTick( [callback] ) 是实例方法。 我们都知道一个页面可以有多个实例, 也就是说this.$nextTick可以精确到某个实例上。 其实本质上两个是一样的。 只是一个是全局,一个是精确到某一个实例。 精确度不一样而已。 

10.使用 nextTick的一个小技巧

我们都知道在生命周期mounted渲染的时候, 不能百分百保证所有的子组件都能够被渲染, 因此我们可以在mounted里面使用 this.$nextTick, 这样就能保证所有的子组件都能被渲染到。  mounted钩子在服务器端渲染期间不被调用。 mounted: function () {   this.$nextTick(function () {     //在数据发生变化,     //重新在视图上完成渲染后,在执行里面的方法     //这一句话等同与:    //将回调延迟到下次 DOM 更新循环之后执行    //等同于:在修改数据之后,然后等待 DOM 更新后在执行   }) }