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>

向 items 数组添加一个新项目:

<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>

焦点似乎在移动……

请看一个说明问题的小提琴https://jsfiddle.net/gu9wyctr/

我明白这种行为一定有充分的理由,但我需要管理它或完全避免。想法?

编辑:

我刚刚意识到我的解释相当模棱两可。这是一个更新的小提琴来说明问题https://jsfiddle.net/keligijus/d1s4mjj7/

问题是输入文本被移动到另一个元素......

我现实生活中的例子。我有一个类似论坛的帖子列表。每个帖子都有一个回复输入。如果有人在其他用户正在输入回复时发布了新帖子,则该用户正在输入的输入将移动到另一个帖子。就像小提琴中的例子一样。

4

2 回答 2

4

Providing key is the answer!

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>

Updated fiddle to show that this works https://jsfiddle.net/keligijus/d1s4mjj7/3/

于 2017-08-06T08:42:54.370 回答
0

尝试这个:

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 相同,然后我可以跟踪焦点项目,即使在更新后也可以再次关注它们。

于 2017-08-06T05:29:36.320 回答