混合Vue阵列道具和数据的正确方法是什么?

时间:2017-12-15 17:12:19

标签: vuejs2 vue-component

我正在尝试创建一个具有'typeahead'方面的组件,并且遇到了一些困难。

组件比此更多,但某些操作会触发父组件将typeaheadItems传递给此组件,此时它应显示它们。用户应该能够隐藏预先输入,这是我遇到问题的地方。

我最初的天真方法如下。

尝试1

鉴于Vue组件:

<template>
    <div
      class="typeahead-items"
      v-show="myTypeaheadItems.length">
        <div class="item close-typeahead">
          <i
            class="icon close"
            @click="closeTypeahead"></i>
        </div>
        <div
          class="item"
          v-for="item in typeaheadItems"
          :key="item">
            {{item}}
        </div>
  </div>
</template>

<script>
  export default {
    name: 'typeahead',
    props: {
      typeaheadItems: {
        type: Array,
        default: function () {
          return [];
        }
      }
    },
    data () {
      return {
      };
    },
    methods: {
      closeTypeahead: function () {
        this.typeaheadItems = [];
      }
    }
  };
</script>

这可以按预期工作,显示和隐藏适当的预先输入,但是给了我警告:

  

避免直接改变道具,因为该值将被覆盖   每当父组件重新渲染时。相反,使用数据或   基于prop值的计算属性。支持变异:   “typeaheadItems”

尝试2

然后我尝试按如下方式更新组件:

v-for="item in myTypeaheadItems"

data () {
      return {
        myTypeaheadItems: this.typeaheadItems
      };
    },
methods: {
      closeTypeahead: function () {
        this.myTypeaheadItems = [];
      }

这根本不起作用,打字从不显示,也没有警告或错误。

尝试3

最后我尝试添加手表:

watch: {
      typeaheadItems: function (val) {
        this.myTypeaheadItems = val.splice(0);
      }
    },

这不起作用并给我警告:

  

你可能在带有表达式的观察者中有一个无限的更新循环   “typeaheadItems”

我似乎无法弄清Vue完成此任务的方法。如何让一个数组道具从另一个组件进入并仍然在其自己的组件中操作它并使视图更新得恰当而没有警告/错误?

更新

进一步审核尝试2 是一种似乎最接近'Vue方式'的方法,它的工作原理是,预先输入的项目正在为它们呈现html元素,但是似乎没有任何事情发生,因为v-show="myTypeaheadItems.length"似乎没有更新。我不确定为什么会出现这种情况,除了可能是Vue网站上列出的caveats之一。

根据Franco Pino下面的评论,我能够尝试3 splice更改为slice 这是向前发展的正确方法吗?

1 个答案:

答案 0 :(得分:1)

(为了诚实,&#34;不要变异道具&#34;消息是你可以忽略的警告,虽然很自然,但一般不会推荐)

问题的简短回答是:

像你在第一个例子中所做的那样用this.myDataVersionOfSomeProp隐藏引用可以使警告消失,但是小心不要丢失那个引用,就像你通过将数据重新分配给其他东西一样,而不是你可以做 someList.length = 0 someList.splice(0)。通过修改而不是重新分配到[]来清空列表。但是我说Vue方式是&#34; Props down,事件结束&#34;。

更长的答案(抱歉长度,说实话):

解决方案一个不起作用,因为最初你将myTypeaheadItems分配给父母传递的道具(因此有一个参考),在你的方法你重新分配它对于别的东西(在这种情况下,重新分配是this.myTypeaheadItems = []),所以你停止引用道具并且孩子myTypeaheadItems与原始道具断开连接。在这里,您可以使用someList.splice(0)清空列表,而无需重新分配整个列表。

你可能意味着在那个观察者中切片而不是拼接。 Splice将修改val,其中包含对原始道具的引用,因此观察者将再次被调用,等等......

至于&#34; Vue方式&#34;:这里相关的Vue座右铭是&#34; 道具倒闭,事件升级&#34; (它与整个&#34;单向流&#34;交易相关)。您将prop从父级传递给子级,如果孩子想要修改它,它会发出一个事件告诉父级修改是有序的,毕竟父组件是拥有该对象的组件,所以它应该自己决定它的修改。

在这里查看自定义事件,你有更多相关的章节(我甚至没有提到计算属性,警告也谈到了):https://vuejs.org/v2/guide/components.html#Custom-Events

这里有一个示例,在子节点中有三种方法来修改来自prop的对象: https://jsfiddle.net/8jwp2mrk/3/

第一个子按钮让子组件通过在其数据中重命名来声明prop的所有权,但它不是直接更改对象的值,而是将其this.myObj重新分配给其他东西(这是在您第一次尝试解决警告问题时。我只是在修改之前将this.myObj重新分配给自己的克隆。 (编辑:要明确的是,由于上一段中解释的原因,这种做法是错误的,我做了这个作为一个例子,说明了孩子&#34; myProp&#34;如何与父母断绝关系&#34; 39; s原始道具,当你希望孩子拥有自己私有版本的传递道具时很有用,但如果你打算让父母和孩子分享一个状态,那么这可能是不受欢迎的行为。)

在第二个中,孩子也将道具重命名为数据myObj,但不是重新分配,而是直接修改它,这可以按预期工作,并且不会发出警告。

第三个跟随&#34;道具失败,事件发生&#34;。当您单击按钮时,子进程告诉父组件它应该更新prop。这也可以按预期工作(您甚至可以在事件中使用参数传递对象应更新的值)。

单击第一个子按钮几次,然后使用底部的按钮隐藏子组件,然后再次单击再次显示它们,您将看到第一个重置为值的值在父母的原始道具中,这是Vue警告,当它告诉你&#34;当父组件重新渲染时,该值将被覆盖&#34;这可能是不合需要的行为,如果您希望孩子更独立地从父母那里管理道具,但仍然与父母分享该道具的价值。