Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue原理问题讨论, 关于vue与原生js混用 #69

Open
afenotes opened this issue Oct 23, 2019 · 2 comments
Open

Vue原理问题讨论, 关于vue与原生js混用 #69

afenotes opened this issue Oct 23, 2019 · 2 comments

Comments

@afenotes
Copy link

afenotes commented Oct 23, 2019

代码如下:
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之间的映射丢了,求指点。
(知道这个混用不对,好奇背后的原理)

@afenotes
Copy link
Author

@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,坑比较多,删除节点的时候会遇到

感谢大神!!!

@xtylovesyj
Copy link

xtylovesyj commented May 3, 2020

<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,这样就可以避免第一个元素不更新的问题了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants