I've been studying Vue's nextTick recently, I wrote myself the following demo code:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const vue = new Vue({
el: '#root',
template: '<div><div id="dom">{{msg}}</div><button @click="add">add</button></div>',
data() {
return {
msg: 1
}
},
watch: {
msg(value) {
console.log('msg>>>>>', value);
}
},
methods: {
add() {
const dom = document.getElementById('dom');
dom.textContent = '30'
for(let i = 0; i < 3; i ++) {
this.msg ++
}
this.$nextTick(() => {
console.log('msg.content2>>>>>>', dom.textContent)
})
}
}
})
</script>
</html>
liveDemo: jsfiddle
the result really confused me.
As i expected, after click the add button, dom.textContent = '30'
change dom element's textConent to 30. and the next for loop, add msg
to 4, as Vue will update page asynchronously, we cant see the newest result in the page. after this.$nextTick
, Vue will use the updated msg
to reRender the dom element immediately, which we know is 4 now. so the dom element's textContent should be 4 now, but it remains 30. why did this happen? where did i get wrong ?
I think I've figured out what's going on here:
When dom.textContent = '30'
is executed, it changes the element's text content to '30'.
But it's not like what I thought before, which directly modifies the textContent of the dom
element's Text child node to the new content. Instead, it generates a new Text
child node and replaces the original one.
Later, in the $nextTick
function, Vue uses the newest msg
value, which is now 4, to update the Text
node's content. However, the Text
node Vue is using now is the one that stored in the el
property, which is the old one, not the updated dom element's Text
child node. As a result, this change is useless, and both the page and the console.log statement fail to show the expected result.
After change the demo code into the following version, I finally get what I want:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const vue = new Vue({
el: '#root',
template: '<div><div id="dom">{{msg}}</div><button @click="add">add</button></div>',
data() {
return {
msg: 1
}
},
watch: {
msg(value) {
console.log('msg>>>>>', value);
}
},
methods: {
add() {
const dom = document.getElementById('dom');
dom.childNodes[0].textContent = '30'; //change dom's child node's textContent, not dom's own textContent;
for(let i = 0; i < 3; i ++) {
this.msg ++
}
this.$nextTick(() => {
console.log('msg.content2>>>>>>', dom.textContent)
})
}
}
})
</script>
</html>
another demo:
change div's textContent creates a new Text node