首页 文章

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

提问于
浏览
0

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

组件比此更多,但某些操作将触发父组件将 typeaheadItems 传递给此组件,此时它应显示它们 . 用户应该能够隐藏类型,尽管我遇到了问题 .

我最初的天真方法如下 .

Attempt 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的值使用数据或计算属性 . Prop变异:“typeaheadItems”

Attempt 2

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

v-for="item in myTypeaheadItems"

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

这根本不起作用,typeahead从不显示,并且没有警告或错误 .

Attempt 3

最后我尝试添加 Watch :

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

这不起作用,并给我警告:

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

我似乎无法弄清楚 Vue 方法来实现这一目标 . 如何让一个数组道具从另一个组件进入并仍然在其自己的组件中操作它并使视图更新而没有警告/错误?

UPDATE

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

鉴于Franco Pino的评论如下,我能够尝试将工作改为 spliceslice . Is this the proper way to move forward?

1 回答

  • 1

    (为了诚实,"don't mutate props"消息是一个警告,你可以忽略,虽然很自然,通常不推荐)

    The short answer to the question is:

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

    Longer answer (对不起,说实话):

    解决方案一不起作用,因为最初你将 myTypeaheadItems 分配给父项传递的道具(因此有一个对它的引用),在你的方法 you are reassigning it to something else (在这种情况下重新分配是 this.myTypeaheadItems = [] ),所以你就停止了对道具的引用和孩子的 myTypeaheadItems 与原始道具断开连接 . 在这里,您可以使用 someList.splice(0) 清空列表,而无需重新分配整个列表 .

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

    至于"The Vue way":这里的相关Vue座右铭是“ Props down, events up " (it's related to the whole "单向流”交易) . 您将prop从父级传递给子级,如果孩子想要修改它,它会发出一个事件告诉父级修改是有序的,毕竟父组件是拥有该对象的组件,所以它应该自己决定它的修改 .

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

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

    第一个子按钮让子组件通过在其数据中重命名来声明prop的所有权,但它不是直接更改对象的值,而是将其__94993_重新分配给其他东西(这是您首次尝试解决的问题)警告 . 对于这个例子,我只是在修改之前将 this.myObj 重新分配给自己的克隆) . (编辑:要清楚,这种方法是错误的,因为在最后一段中解释的原因,我做了这个作为一个孩子's 594995 might become disconnected from the parent'的原始道具的例子,当你想要孩子拥有自己的私人版本时这很有用如果你打算让父母和孩子分享一个状态,那么可能是不受欢迎的行为

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

    第三部分是“道具倒塌,事件发生” . 当您单击按钮时,子进程告诉父组件它应该更新prop . 这也可以按预期工作(您甚至可以在事件中使用参数传递对象应更新的值) .

    单击第一个孩子按钮几次,然后使用底部的按钮隐藏子组件,然后再次单击再次显示它们,您将看到第一个的值被重置为父级中原始道具的值, this is what Vue warns against 当它告诉你"the value will be overwritten whenever the parent component re-renders"时,如果你期望孩子更加独立于父母来管理道具,但仍然与父母分享该道具的 Value ,这可能是不良行为 .

相关问题