【问题标题】:What is the correct way to blend Vue array props and data?混合 Vue 数组道具和数据的正确方法是什么?
【发布时间】:2018-05-29 22:26:57
【问题描述】:

我正在尝试创建一个具有“预先输入”方面的组件,但遇到了一些困难。

该组件还有更多内容,但某些操作会触发父组件将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 的cmets,我能够尝试3splice 更改为slice这是前进的正确方式吗?

【问题讨论】:

    标签: vuejs2 vue-component


    【解决方案1】:

    (老实说,“不要改变道具”消息是您可以忽略的警告,尽管通常不推荐这样做)

    问题的简短回答是:

    像在第一个示例中那样使用this.myDataVersionOfSomeProp 隐藏引用可以使警告消失,但请注意不要像通过将数据重新分配给其他东西那样丢失该引用,而是可以执行 @ 987654327@ someList.splice(0)。通过修改而不是重新分配给[] 来清空列表。但我会说 Vue 的方式是“Props down, events up”。

    更长的答案(对不起,说实话):

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

    您可能在那个观察者中指的是切片而不是拼接。 Splice 会修改 val,它引用了原始的 prop,所以 watcher 会被再次调用,以此类推...

    至于“Vue 方式”:这里的相关 Vue 座右铭是“Props down, events up”(它与整个“单向流程”交易有关)。您将 prop 从父组件传递给子组件,如果子组件想要修改它,它会发出一个事件,告诉父组件修改是有序的,毕竟父组件是拥有该对象的组件,所以它应该自己决定修改。

    请参阅此处了解自定义事件,那里有更多相关章节(我什至没有提到计算属性,警告也谈到了):https://vuejs.org/v2/guide/components.html#Custom-Events

    这是一个示例,其中包含三个子项中用于修改来自道具的对象的方法: https://jsfiddle.net/8jwp2mrk/3/

    第一个子按钮的子组件通过在其数据中重命名 prop 来声明对 prop 的所有权,但不是直接更改对象的值,而是将其 this.myObj 重新分配给其他东西(这是你第一次尝试解决警告。对于这个例子,我只是在修改之前将this.myObj重新分配给它自己的克隆)。 (编辑:需要明确的是,由于上一段中解释的原因,这种方法是错误的,我这样做是为了说明孩子的“myProp”如何与父母的原始道具断开连接,这在您想要子拥有自己的私有版本的传递道具,但如果您希望父母和孩子共享一个状态,则可能是不受欢迎的行为)

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

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

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

    【讨论】:

    • 非常感谢您的冗长回答。我现在才能够尝试一下。您的长度建议很有趣,因为它似乎与 Vue 的文档相矛盾:vuejs.org/v2/guide/list.html#Caveats 另外,一个对象必须通知其父级(反过来会通知子级)以清除自身(这就是您的意思)似乎很奇怪“道具下降,事件上升”?)。不过,我将尝试研究您的其他建议。谢谢。
    • 啊,忘了注意事项2,但是你是对的,使用splice清空列表,肯定有使用.length不会按预期重新渲染的场景。 >“这反过来会通知孩子”因为观察者?不,如果使用事件,父组件会进行清除,而不是观察者中的子组件。我是这样看的:父组件是对象的所有者,它的工作是改变或改变它。孩子只是使用(显示它,或基于它计算计算属性等)或观察它(通过观察者对它的变化做出反应)。
    • Vue 喜欢单向数据流(相关文档:vuejs.org/v2/guide/components.html#One-Way-Data-Flowvuejs.org/v2/guide/components.html)。让一个组件负责 prop 的值可以将子组件与父组件分离并使其更易于理解,因此有关其更新的信息只会“向下流动”。 prop 仅在父级中发生变化,而不是任意数量的任意和遥远的组件,这使得很难知道它发生了什么。事件增加,因此孩子可以告诉父母父母是否应该对某些事情做出反应(例如,通过更新其状态)
    猜你喜欢
    • 2021-08-24
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 2020-12-18
    • 1970-01-01
    • 2021-08-08
    • 2017-09-09
    • 1970-01-01
    相关资源
    最近更新 更多