Vue原理问题讨论, 关于vue与原生js混用
afenotes opened this issue · 2 comments
代码如下:
https://jsfiddle.net/afenotes/bkjxvr07/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VUE</title>
<script src="https://unpkg.com/jquery@1.12.4"></script>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<ul id="list">
<li v-for="(l,index) in list" v-bind:key="index" :ref='"li"+index'>{{l}}</li>
</ul>
<script>
var ul = new Vue({
el: '#list',
data: {
list: ['apple','banana','orange']
}
})
setTimeout(function(){
// $('<li>grape</li>').prependTo($('#list'))
$('#list').children().first().text('grape')
}, 3000);
setTimeout(function(){
ul.list = ['Bob','Tom','Jim']
}, 6000);
</script>
</body>
</html>
初始渲染结果:
- apple
- banana
- orange
3s后,渲染结果:
- grape
- banana
- orange
6s后,实际渲染结果:
- grape
- Tom
- Jim
期望结果:
- Bob
- Tom
- Jim
为什么第一个元素没有更新?猜测virtual dom到真实dom之间的映射丢了,求指点。
(知道这个混用不对,好奇背后的原理)
@ustbhuangyi 大神回复如下:
你这个问题的原因定位了,首先 li 包裹的文本元素会在 createElm 阶段创建 vnode.elm = nodeOps.createTextNode(vnode.text),然后你用 jQuery 修改了这个文本节点,文本就已经变了。由于你的 v-for 的 key 是 index,那么在 patch 过程中,新旧 li 始终被认为是 sameVnode,也就会执行 patchVnode 方法更新,对于文本节点,最后会更新
else if (oldVnode.text !== vnode.text) {
nodeOps.setTextContent(elm, vnode.text);
}
但是这个时候这个 elm 是之前 oldVnode.elm,它的文本是 'apple',但是现在界面上显示的是用 jQ 修改的文本节点 grape,也就是说你只是把 apple 修改成了 Bob,但实际上界面上的 grape 你并未修改,这就是 DOM 操作和 Vue 混用的坑。
这里你可以把 v-for 的 key 改成 l,这样的话再更新的过程中新旧 li 就不是 sameVnode 了,它就会创建新节点,删除旧节点,就可以正常更新了
通常是不建议 v-for 的 key 用 index,坑比较多,删除节点的时候会遇到
感谢大神!!!
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VUE</title>
<script src="https://unpkg.com/jquery@1.12.4"></script>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<ul id="list">
<li v-for="(l,index) in list" v-bind:key="index" :ref='"li"+index'>{{l}}</li>
</ul>
<script>
var ul = new Vue({
el: '#list',
data: {
list: ['apple', 'banana', 'orange']
}
})
setTimeout(function () {
// $('<li>grape</li>').prependTo($('#list'))
$('#list').children().first()[0].childNodes[0].textContent = 'grape';
}, 3000);
setTimeout(function () {
ul.list = ['Bob', 'Tom', 'Jim']
}, 6000);
</script>
</body>
</html>
这个问题很有趣,原理就像大神所说的之前的文本节点对象已经被jquery给更改了,所以为了不让其更改文本节点对象,可以直接更改文本节点的textContent,这样就可以避免第一个元素不更新的问题了