Vue反应性,防止使用输入v模型更新道具更新时的重新渲染组件

时间:2020-09-02 23:33:13

标签: javascript vue.js

我正在尝试锻炼如何在不使组件重新呈现的情况下使文本输入中的道具发生变化。

这是我的代码

//App.vue

<template>
    <div class="row">
        <category-component v-for="category in categories" :key="category.title" :category="category" />
    </div>
</template>


export default {
  name: "App",
  data() {
    return {
      categories: [
        {
          title: "Cat 1",
          description: "description"
        },
        {
          title: "Cat 2",
          description: "description"
        },
        {
          title: "Cat 3",
          description: "description"
        }
      ]
    };
  },
  components: {
    CategoryComponent
  }
}
// CategoryComponent.vue

<template>
    <div>
        <input type="text" v-model="category.title">
        <br>
        <textarea v-model="category.description"></textarea>
    </div>
</template>

当我更新文本输入时,该组件重新渲染,并且失去焦点。但是,当我更新文本区域时,它不会重新渲染。

有人能对此有所启示吗?很明显,我缺少与vue反应性简单的东西。

这是我问题的基本codesandbox

1 个答案:

答案 0 :(得分:2)

您遇到的问题是由:key="category.title"引起的。选中Vue Guide: key

关键特殊属性主要用作Vue的提示 虚拟DOM算法,用于在比较新列表时识别VNode 节点对照旧列表。没有密钥,Vue使用的算法 最小化元素移动并尝试修补/重用元素 尽可能就地使用相同类型。有了钥匙,它将重新排序 基于键顺序更改的元素,以及带有键的元素 不再存在的内容将始终被删除/破坏。

更改标题时,将更改键,然后将销毁VNode并创建它,这就是为什么它失去焦点并且更改摘要时不会失去焦点的原因。

因此,解决方法是使用index的第二个参数= v-for作为键。

不要直接改变道具,因此在下面的代码片段中,我使用了一个计算的设置器来发出一个输入事件,以通知父组件更新绑定值(通过v-model)。

您还可以检查this question: updating-a-prop-inside-a-child-component-so-it-updates-on-the-parent以获得有关更新组件内部道具的更多详细信息。

Vue.component('category-component', {
    template:`
      <div class="hello">
        <input type="text" v-model="innerTitle">
        <br>
        <textarea v-model="innerSummary"></textarea>
      </div>
    `,
    props: {
      value: {
        default: () => {return {}},
        type: Object
      }
    },
    computed: {
      innerTitle: {
        get: function () {
          return this.value.title
        },
        set: function (val) {
          this.$emit('input', {summary: this.value.summary, title: val})
        }
      },
      innerSummary: {
        get: function () {
          return this.value.summary
        },
        set: function (val) {
          this.$emit('input', {title: this.value.title, summary: val})
        }
      }
    }
})

new Vue ({
  el:'#app',
  data () {
    return {
      categories: [
        {
          title: "Cat 1",
          summary: "description"
        },
        {
          title: "Cat 2",
          summary: "description"
        },
        {
          title: "Cat 3",
          summary: "description"
        }
      ]
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
  <div id="app">
    {{ categories }}
    <category-component v-for="(category, index) in categories" :key="index" v-model="categories[index]" />
  </div>

相关问题