首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >复制操作后Vue组件的错误顺序

复制操作后Vue组件的错误顺序
EN

Stack Overflow用户
提问于 2019-03-21 09:45:12
回答 3查看 573关注 0票数 2

我有几个手风琴(每个手风琴都是一个Vue组件),默认情况下它们是扩展的。还有一个“复制”功能,允许复制每个组件。

代码语言:javascript
复制
Vue.component("Accordion", {
  template: "#accordion-template",
  
  data: function () {
    return {
      open: true
    }
  },
  
  methods: {
    toggle: function () {
      this.open = !this.open;
    }
  }
});


new Vue({
  el: '#vue-root',
  data: {
    devices: [
      {
        name: "a", description: "first"
      },
      {
        name: "b", description: "second"
      },
      {
        name: "c", description: "third"
      }
    ]
  },

  methods: {
    copy: function (device) {
      var index = this.devices.indexOf(device) + 1;
      var copy = {
          name: device.name + "_copy",
          description: device.description + "_copy"
      };

      this.devices.splice(index, 0, copy);
    }
  }
});
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<div id="vue-root">
    <div class="device" v-for="device in devices">
        <accordion>
            <div slot="acc-head">
                <span>{{ device.name }}</span><br/>
                <button @click="copy(device)">copy</button>
            </div>
            <div slot="acc-body">
                {{ device.description }}
            </div>
        </accordion>
    </div>
</div>

<script type="text/x-template" id="accordion-template">
    <div>
        <slot name="acc-head"></slot>
        <button @click="toggle">Open: {{ open }}</button>
        <div :class="open ? 'active' : 'hidden'">
            <slot name="acc-body"></slot>
        </div>
    </div>
</script>

当所有手风琴都被折叠(换句话说,'open: false')并且我试图从列表中间复制手风琴(例如b)时,我希望出现名为'name'_copy的新组件,它必须在默认情况下展开。但是,新组件的所有属性的值与重复的属性值相同,列表中的最后一个组件将被展开。

我怎样才能解决这个问题?

小提琴:https://jsfiddle.net/j3ydt1m7/

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-03-21 10:59:04

简短回答

v-for循环中添加一个键:v-for="device in devices" :key="{something here}"。您的密钥必须是唯一的,并且识别每个设备,即使是在设备复制之后。

代码

请查收:un/9 9cradxvp/。为了调试的目的,我改变了一些东西:

  • 我将device作为props of <accordion>,以便在console.log中使用设备属性
  • 复制设备现在从<accordion>发出。关于侦听子组件事件的Vue doc
  • 我添加了mounted()updated()钩子。更多关于生命周期钩的信息
  • 每个设备都有一个ID

长答案

关于列表呈现

如果在v-for循环中没有提供密钥,Vue不知道如何更新列表。来自Vue文档

为了给Vue一个提示,以便它能够跟踪每个节点的标识,从而重用和重新排序现有的元素,您需要为每个项提供一个唯一的键属性。

让我们考虑一下您的列表(我添加了一个元素)

代码语言:javascript
复制
[
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
]

现在,让我们复制节点"b“。如果没有:key="device.id",控制台输出是

代码语言:javascript
复制
4: d is mounted
3: c is updated
5: b_copy is updated

对于:key="device.id",控制台输出仅为:

代码语言:javascript
复制
5: b_copy is mounted

基本上,没有钥匙,就有:

  • 两个更新:c变成b_copyd变成c
  • 一个插入:创建d

因此,每次处理副本时都会重新创建最后一个元素。由于open默认值是true,显然,dopen = true

如果每个元素都有一个:key="device.id",那么只创建元素b_copy

要检查这一点,请从我的小提琴中移除:key="device.id",并查看控制台中发生了什么。

选择密钥

由于键必须唯一地标识设备,因此当您复制设备时,不应将索引用作数组更改中的设备索引。

此外,首选ID字段,因为无法保证设备名称是唯一的。如果您初始化列表时

代码语言:javascript
复制
[
  { name: "a"},
  { name: "b"},
  { name: "a"}
]

从功能的角度来看,这是正确的。

票数 3
EN

Stack Overflow用户

发布于 2019-03-21 10:42:15

在使用Vue和lists时,应该向带有Vue的元素中添加一个钥匙支柱。使用这样的键,让我们知道您指的是一个特定的元素。

代码语言:javascript
复制
    <div class="device" v-for="device in devices" :key="device.name">

我认为原因在于,由于性能原因,Vue默认添加一个新元素作为最后一个元素,然后更新其他节点中的数据。因此,添加的新元素实际上是列表中的最后一个元素,其打开集为true。

票数 3
EN

Stack Overflow用户

发布于 2019-03-21 11:02:16

在您的代码中添加一点:

您可以直接传递索引,而不是提供"device“对象并搜索其索引。

这就是我的意思:jsFiddle

代码语言:javascript
复制
Vue.component("Accordion", {

  template: "#accordion-template",

  data: function() {
    return {
      open: true
    }
  },

  methods: {
    toggle: function() {
      this.open = !this.open;
    }
  }
});


new Vue({
  el: '#vue-root',
  data: {
    devices: [{
        name: "a",
        description: "first"
      },
      {
        name: "b",
        description: "second"
      },
      {
        name: "c",
        description: "third"
      },
    ]
  },

  methods: {
    copy: function(index) {
      var device = this.devices[index];
      var copy = {
        name: device.name + "_copy",
        description: device.description + "_copy"
      };

      this.devices.splice(index + 1, 0, copy);
    },
    remove: function(index) {
      this.devices.splice(index, 1);
    }
  }
});
代码语言:javascript
复制
.device {
  margin: 10px 0;
}

.active {
  display: block;
}

.hidden {
  display: none;
}

div.device {
  border: 1px solid #000000;
  box-shadow: 1px 1px 2px 1px #a3a3a3;
  width: 300px;
  padding: 5px;
}
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vue-root">
  <div class="device" v-for="(device, index) in devices" :key="device.name">
    <accordion>
      <div slot="acc-head">
        <span>{{ device.name }}</span><br/>
        <button @click="copy(index)">copy</button>
        <button @click="remove(index)">remove</button>
      </div>
      <div slot="acc-body">
        {{ device.description }}
      </div>
    </accordion>
  </div>
</div>

<script type="text/x-template" id="accordion-template">
  <div>
    <slot name="acc-head"></slot>
    <button @click="toggle">Open: {{ open }}</button>
    <div :class="open ? 'active' : 'hidden'">
      <slot name="acc-body"></slot>
    </div>
  </div>
</script>

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55277559

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档