VueJS v-for 不需要的行为
VueJS v-for unwanted behaviour
每当我修改用于呈现 v-for 列表的数组时,我都会遇到这个问题。
假设我有一个包含三个项目的 v-for 列表:
<ul>
<li v-for="item in items"></li>
<ul></ul>
<ul>
<li>One</li> <!-- Has focus or a specific child component -->
<li>Two</li>
<li>Three</li>
</ul>
向项目数组添加一个新项目:
<ul>
<li>New Item</li> <!-- Focuses on this item, the child component seems to be moved here -->
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
焦点似乎在移动...
请查看说明问题的 fiddle https://jsfiddle.net/gu9wyctr/
我明白这种行为一定有充分的理由,但我需要管理它或完全避免。想法?
编辑:
我刚刚意识到我的解释相当含糊。这是一个更新的 fiddle 来说明问题 https://jsfiddle.net/keligijus/d1s4mjj7/
问题是输入文本被移动到另一个元素...
我的真实例子。我有一个类似论坛的 post 列表。每个 post 都有一个回复输入。如果某人在其他用户输入回复时发布了新 post,则该用户输入的内容将移至另一个 post。就像fiddle.
中的例子
试试这个:
var app = new Vue({
el: '#app',
data: {
messages: [
{ message: 'Hello Vue!', id: 0 },
{ message: 'Hello Vuex!', id: 1 },
{ message: 'Hello VueRouter!', id: 2 }
],
msg: null,
focus: 'item-1'
},
mounted () {
document.getElementById(this.focus).focus()
setTimeout(() => {
this.messages.unshift({ message: 'Focus moves!', id: 3 })
}, 2000)
setTimeout(() => {
this.messages.unshift({ message: 'Moves again...', id: 4 })
this.msg = `I suppose this happens because of the way DOM is updated and I understand there must a good reason for this. However I need to avoid this behaviour. How can I do this?`
}, 4000)
},
updated: function () {
document.getElementById(this.focus).focus()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<ul>
<li v-for="(message, index) in messages">
<input :id="'item-'+message.id" type="text" v-model="message.message" style="width:80%;">
</li>
<li v-if="msg">{{msg}}</li>
</ul>
</div>
基本上,即使添加了新项目,我也会使 id 相同,然后我可以跟踪聚焦的项目,即使在更新后再次聚焦它们。
提供密钥就是答案!
https://vuejs.org/v2/guide/list.html#key
When Vue is updating a list of elements rendered with v-for, it by default uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will simply patch each element in-place and make sure it reflects what should be rendered at that particular index. This is similar to the behavior of track-by="$index" in Vue 1.x.
This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state (e.g. form input values).
To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item. An ideal value for key would be the unique id of each item. This special attribute is a rough equivalent to track-by in 1.x, but it works like an attribute, so you need to use v-bind to bind it to dynamic values (using shorthand here):
<li v-for="(item, index) in items" :key="'item-'+item">
<input :id="'item-'+index" type="text" style="width:80%;">
</li>
已更新 fiddle 以表明这有效 https://jsfiddle.net/keligijus/d1s4mjj7/3/
每当我修改用于呈现 v-for 列表的数组时,我都会遇到这个问题。
假设我有一个包含三个项目的 v-for 列表:
<ul>
<li v-for="item in items"></li>
<ul></ul>
<ul>
<li>One</li> <!-- Has focus or a specific child component -->
<li>Two</li>
<li>Three</li>
</ul>
向项目数组添加一个新项目:
<ul>
<li>New Item</li> <!-- Focuses on this item, the child component seems to be moved here -->
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
焦点似乎在移动...
请查看说明问题的 fiddle https://jsfiddle.net/gu9wyctr/
我明白这种行为一定有充分的理由,但我需要管理它或完全避免。想法?
编辑:
我刚刚意识到我的解释相当含糊。这是一个更新的 fiddle 来说明问题 https://jsfiddle.net/keligijus/d1s4mjj7/
问题是输入文本被移动到另一个元素...
我的真实例子。我有一个类似论坛的 post 列表。每个 post 都有一个回复输入。如果某人在其他用户输入回复时发布了新 post,则该用户输入的内容将移至另一个 post。就像fiddle.
中的例子试试这个:
var app = new Vue({
el: '#app',
data: {
messages: [
{ message: 'Hello Vue!', id: 0 },
{ message: 'Hello Vuex!', id: 1 },
{ message: 'Hello VueRouter!', id: 2 }
],
msg: null,
focus: 'item-1'
},
mounted () {
document.getElementById(this.focus).focus()
setTimeout(() => {
this.messages.unshift({ message: 'Focus moves!', id: 3 })
}, 2000)
setTimeout(() => {
this.messages.unshift({ message: 'Moves again...', id: 4 })
this.msg = `I suppose this happens because of the way DOM is updated and I understand there must a good reason for this. However I need to avoid this behaviour. How can I do this?`
}, 4000)
},
updated: function () {
document.getElementById(this.focus).focus()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<ul>
<li v-for="(message, index) in messages">
<input :id="'item-'+message.id" type="text" v-model="message.message" style="width:80%;">
</li>
<li v-if="msg">{{msg}}</li>
</ul>
</div>
基本上,即使添加了新项目,我也会使 id 相同,然后我可以跟踪聚焦的项目,即使在更新后再次聚焦它们。
提供密钥就是答案!
https://vuejs.org/v2/guide/list.html#key
When Vue is updating a list of elements rendered with v-for, it by default uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will simply patch each element in-place and make sure it reflects what should be rendered at that particular index. This is similar to the behavior of track-by="$index" in Vue 1.x.
This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state (e.g. form input values).
To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item. An ideal value for key would be the unique id of each item. This special attribute is a rough equivalent to track-by in 1.x, but it works like an attribute, so you need to use v-bind to bind it to dynamic values (using shorthand here):
<li v-for="(item, index) in items" :key="'item-'+item">
<input :id="'item-'+index" type="text" style="width:80%;">
</li>
已更新 fiddle 以表明这有效 https://jsfiddle.net/keligijus/d1s4mjj7/3/