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

Vuejs2 混合Vue阵列道具和数据的正确方法是什么?,vuejs2,vue-component,Vuejs2,Vue Component,我正在尝试创建一个具有“typeahead”特性的组件,但遇到了一些困难 组件的功能远不止这些,但某些操作会触发父组件将typeaheatems传递到此组件,此时它应该显示它们。用户应该能够隐藏typeahead,尽管这正是我遇到问题的地方 我最初天真的做法如下 尝试1 给定Vue组件: <template> <div class="typeahead-items" v-show="myTypeaheadItems.length">

我正在尝试创建一个具有“typeahead”特性的组件,但遇到了一些困难

组件的功能远不止这些,但某些操作会触发父组件将
typeaheatems
传递到此组件,此时它应该显示它们。用户应该能够隐藏typeahead,尽管这正是我遇到问题的地方

我最初天真的做法如下

尝试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>
这根本不起作用,typeahead从不显示,也没有警告或错误

尝试3次

最后,我尝试添加一块手表:

watch: {
      typeaheadItems: function (val) {
        this.myTypeaheadItems = val.splice(0);
      }
    },
这不起作用,给我一个警告:

您可能在watcher中有一个带有表达式的无限更新循环 “类型标题”

我似乎想不出实现这一点的
Vue
方法。如何让阵列道具从另一个组件进入,并且仍然在其自己的组件中操作它,并在没有警告/错误的情况下适当更新视图

更新

进一步检查后,尝试2似乎是最接近“Vue方式”的方法,它的工作原理是,typeahead项正在为它们呈现html元素,但似乎什么都没有发生,因为
v-show=“myTypeaheadItems.length”
似乎没有更新。除了Vue网站上可能列出的一种情况外,我不确定为什么会出现这种情况

根据下面的注释,我可以尝试3将
splice
更改为
slice
这是前进的正确方式吗?

(为了诚实起见,“不要改变道具”信息是一个警告,您可以忽略它,当然,一般不建议这样做)

这个问题的简短答案是:

像在第一个示例中那样使用
this.myDataVersionOfSomeProp
隐藏引用可能会使警告消失,但请注意不要像将数据重新分配给其他对象那样丢失引用,相反,您可以执行
someList.length=0
someList.splice(0)
。通过修改而不是重新分配到
[]
来清空列表。但我要说的是,Vue的方式是“道具放下,活动起来”

更长的回答(老实说,长度很抱歉):

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

你可能是指在那个观察者身上的切片而不是拼接。Splice将修改
val
,其中包含对原始道具的引用,因此观察者将再次被调用,依此类推

至于“Vue方式”:这里相关的Vue座右铭是“道具放下,事件上升”(这与整个“单向流”交易有关)。您将道具从父对象传递给子对象,如果子对象想要修改道具,它将发出一个事件,告诉父对象修改已就绪。毕竟,父组件是拥有该对象的组件,因此它应该自行决定其修改

有关自定义事件,请参见此处,其中有更多相关章节(我甚至没有提到警告中也提到的计算属性):

下面是一个示例,其中有三种方法用于修改来自道具的对象:

第一个子按钮的子组件通过在其数据中重命名道具来声明道具的所有权,但它不是直接更改对象的值,而是将其
this.myObj
重新指定给其他对象(这是您第一次尝试解决警告时遇到的问题。在本例中,我只是在修改之前将
this.myObj
重新分配给自身的克隆)。(编辑:为了澄清,由于上一段中解释的原因,这种方法是错误的,我将此方法作为一个儿童“myProp”的示例可能与父级的原始道具断开连接,这在您希望子级拥有其自己的私有版本的传递道具时非常有用,但如果您希望父级和子级共享一个状态,则可能是不希望的行为)

在第二个例子中,子对象还将道具重命名为data
myObj
,但它不是重新分配道具,而是直接修改道具,这与预期一样有效,不会收到任何警告

第三个在“道具向下,事件向上”之后。当您单击按钮时,子组件告诉父组件它应该更新道具。这也可以按预期工作(您甚至可以在事件中使用参数来传递对象应该更新到的值)


单击第一个子按钮几次,然后使用底部的按钮隐藏子组件,然后再次单击以再次显示它们,您将看到第一个被重置为父级中原始道具值的子组件的值,这是Vue在告诉您“每当父组件重新渲染时,该值将被覆盖“,如果您希望孩子更独立于家长管理道具,但仍然与家长分享道具的价值,这可能是不可取的行为。

非常感谢您冗长的回答。我刚刚才能够尝试一下。您的长度建议很有趣,因为它似乎与Vue的文档相矛盾:
watch: {
      typeaheadItems: function (val) {
        this.myTypeaheadItems = val.splice(0);
      }
    },