這裡是前面那部分的一個小番外,再處理實體的狀態同步到DOM的時候雖然我們一直在說"同步"更新,但在執行上它還是非同步的,當setter偵測到需要更新的狀態時會將事件加入到一個排隊queue中,我們可以用這個例子來看
<div id="app">
<p>
messages: {{ messages }}
</p>
<p>
scrollHeight: {{ scrollHeight }} <br>
DOM 實際的 scrollHeight: {{ realScrollHeight }}
</p>
<div class="messages">
<div v-for="m in messages">{{ m }}</div>
</div>
<input type="text" placeholder="輸入任意文字後按下 enter 鍵" v-model.trim="msg" @keydown.enter="addToMessages">
</div>
const vm = Vue.createApp({
data: () => {
return {
msg: '',
scrollHeight: 0,
realScrollHeight: 0,
messages: ['Hello', 'Vue.js', '好棒棒']
}
},
methods: {
addToMessages() {
this.messages.push(this.msg);
this.msg = '';
const el = document.querySelector('.messages');
this.scrollHeight = el.scrollHeight;
el.scrollTop = el.scrollHeight;
this.$nextTick(() => {
this.realScrollHeight = el.scrollHeight;
});
}
},
mounted() {
const el = document.querySelector('.messages');
this.realScrollHeight = el.scrollHeight;
this.scrollHeight = el.scrollHeight;
}
});
vm.mount('#app');
這個程式是我們希望在user輸入完成後按下enter鍵,會將文字data新增到一個文字陣列當中,並且讓畫面自動移動到最底端來顯示最新加入的訊息,上面的addToMessage()就是這個功能,但上面這樣的寫法雖然看起來沒問題,執行起來卻發現它始終會滑到和最後一行有一行差距的位置,這就是因為狀態與畫面的更新"不同步"的原因,它在新的文字被加到畫面之前就先偵測高度,自然得到的結果會是上一行,這個問題只要加上vm.$nextTick(),就可以確保它會等到畫面的更新都結束了才執行。