從 Vue 執行個體生命周期了解 nextTick api 原理及用法
前兩天在開發時遇到一個需求:打開對話框的時候自動聚焦其中的輸入框。由于原生的 autofocus 屬性不起作用,需要使用元件庫提供的
focus
方法手動手動擷取焦點。于是有如下代碼: <el-button @click="openDialog">點選打開 Dialog</el-button>
<el-dialog :visible.sync="dialogVisible">
<el-input v-model="input" ref="input"></el-input>
</el-dialog>
methods: {
openDialog() {
this.dialogVisible = true;
const input = this.$refs.input;
input.focus();
},
},
結果報錯了,原因是沒有擷取到 input 元件;通過 log,也驗證了
this.$refs.input
的值确實是
undefined
。但是經過測試,如果對話框預設狀态是打開的,就不會報錯;明明元件就在那,為什麼擷取不到呢?
生命周期 update
經過分析,這種現象是由于 Vue 執行個體的更新機制造成的。從下方的生命周期圖(局部)中可以看出,元件裝載好之後,遇到資料變化時将重新渲染虛拟 DOM(可以了解為 HTML 中的元件節點)。在本例中,隐藏的 Dialog 元件(以及其中的 input 元件)本來并沒有渲染在 DOM 中,是在觀察到
dialogVisible
屬性變為 true 後再進行更新渲染的。

而網頁渲染通常是一個異步任務,是以在 visible 屬性剛剛更改時(一個函數中是同步過程),DOM 渲染還沒有進行,是以自然擷取不到此時還不存在的 input 元件了。
關于異步、JS任務隊列、宏任務與微任務等概念的更多介紹,可參考博文JS多線程:任務隊列
為了更直覺地展示這個過程,可以在更新前後的鈎子函數中試圖擷取元件并進行列印:
beforeUpdate() {
console.log("beforeUpdate");
const input = this.$refs.input;
console.log(input);
},
updated() {
console.log("updated");
const input = this.$refs.input;
console.log(input);
},
methods: {
openDialog() {
this.dialogVisible = true;
console.log("click open");
},
},
結果如下,可以驗證之前的分析和猜想:
click open
beforeUpdate
undefined
updated
VueComponent {...}
Vue.nextTick
為了解決這個問題,Vue 提供了全局 api
Vue.nextTick()
,它的作用是提供下次 DOM 更新之後的回調。也就是說,在更新資料後調用 api,就能夠擷取到重新渲染後的 DOM 并進行相關操作。
nextTick 方法可以廣泛适用于各種需要在資料更新後對相關 DOM 進行操作的情景,例如
v-if
、
watch
等。
在上文的例子中再加入 nextTick:
openDialog() {
this.dialogVisible = true;
console.log("click open");
this.$nextTick(function () {
console.log("next tick");
const input = this.$refs.input;
console.log(input);
input.focus();
});
},
可以看到,回調确實是在 DOM 更新之後,也就是 updated 執行之後才執行的。擷取元件與手動獲得焦點的操作也能夠正确執行了。
click open
beforeUpdate
undefined
updated
VueComponent {...}
next tick
VueComponent {...}
Promise
如果沒有提供回調參數,并且浏覽器支援 Promise,調用 nextTick 将傳回一個 Promise。也就是說下面幾種寫法是等價的(環境支援的情況下):
Vue.nextTick(function () {...})
Vue.nextTick(() => {...})
Vue.nextTick().then(function () {...})
Vue.nextTick().then(() => {...})
關于 Promise 的介紹和用法,可以參考博文 JS Promise。
結語&參考資料
以上是個人對 Vue 中 nextTick api 的一些了解與思考,希望能給你提供幫助。如果有問題或疏漏之處,歡迎在評論中讨論與指正。
我将繼續在個人部落格中更新自己的學習筆記,以前端技術(Vue架構)為主,感興趣的話歡迎關注!
參考資料:
Vue 文檔 - api
Vue 文檔 - 執行個體