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>

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>

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>

我們發現,第一次的值和第二次的值,是不一樣的;
因為視圖上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 更新后在執行
})
}