nextTick原始碼分析:MutationObserver和MessageChannel
阿新 • • 發佈:2020-11-16
為什麼要用nextTick
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>example</title> </head> <body> <div id="app"> <div v-if="isShow"> <input type="text" ref="userName" /> </div> <button @click="showInput">點選顯示輸入框</button> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { isShow: false }, methods:{ showInput(){ this.isShow = true this.$refs.userName.focus() } } }) </script>
執行結果是報錯,找不到節點。也就是說,當你執行到isShow=true時,此時dom節點尚未更新,只能等待dom更新後,你才能執行下面的focus。
用MessageChannel實現
<!DOCTYPE html> <html lang="en-zh"> <head> <meta charset="utf-8" /> <style type="text/css"> </style> </head> <body> <div id="app"> <div v-if="isShow"> <input type="text" ref="userName" /> </div> <button @click="showInput">點選顯示輸入框</button> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { isShow: false }, methods: { showInput(){ this.isShow = true; this.myNextTick(() => { this.$refs.userName.focus(); }) }, myNextTick(fanc){ var that = this; const ch = new MessageChannel(); const port1 = ch.port1; const port2 = ch.port2; port2.onmessage = (() => { fanc(); }) port1.postMessage(1); } } }) </script>
用MutationObserver實現
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>example</title> </head> <body> <div id="app"> <div v-if="isShow"> <input type="text" ref="userName" /> </div> <button @click="showInput">點選顯示輸入框</button> </div> </body> </html> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { isShow: false }, methods:{ showInput(){ this.isShow = true this.mynextTick(()=>{ this.$refs.userName.focus() }) }, mynextTick(func){ var textNode = document.createTextNode(0)//新建文字節點 var that = this var callback = function(mutationsList, observer) { func.call(that); // 或 // fanc(); } var observer = new MutationObserver(callback); observer.observe(textNode,{characterData:true }) textNode.data = 1//修改文字資訊,觸發dom更新 } } }) </script>
為何棄用MessageChannel使用MutationObserver
vue2中在2018年12月20號用MutationObser替換了MessageChannel(有很多部落格說是相反的,但查git上的時間線是這樣的),把2個都使用下對比便知結果:
因為MutationObserver是巨集任務,MessageChannel是微任務,比它先執行,選用它肯定考慮了這部分因素。
<!DOCTYPE html>
<html lang="en-zh">
<head>
<meta charset="utf-8" />
<style type="text/css">
</style>
</head>
<body>
<div id="app">
<div v-if="isShow">
<input type="text" ref="userName" />
</div>
<button @click="showInput">點選顯示輸入框</button>
</div>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
isShow: false
},
methods: {
showInput(){
this.isShow = true;
this.myNextTick1(() => {
this.$refs.userName.focus();
})
this.myNextTick2(() => {
this.$refs.userName.focus();
})
},
myNextTick1(fanc){
var that = this;
const ch = new MessageChannel();
const port1 = ch.port1;
const port2 = ch.port2;
port2.onmessage = (() => {
console.log('1')
fanc();
})
port1.postMessage(1);
},
myNextTick2 (fanc) {
var that = this;
var textNode = document.createTextNode('0');
var callback = function(mutationList, observer){
console.log('2')
fanc();
}
var observer = new MutationObserver(callback);
observer.observe(textNode, {characterData: true});
textNode.data = 1;
}
}
})
</script>
打印出來:
2
1
並不是按順序執行,而是先巨集任務MutationObserver,再微任務MessageChannel。