Vue.js 更新的属性不会在子组件上更改

Posted

技术标签:

【中文标题】Vue.js 更新的属性不会在子组件上更改【英文标题】:Vue.js updated property doesn't change on child component 【发布时间】:2020-12-19 20:33:24 【问题描述】:

我有一个子组件,它是一个 html 输入元素。它看起来像这样:

<label-input inputId="formProductId"
             inputType="number"
             inputName="productId"
             :inputEnabled="filters.productId.options"
             :label="translations['product_id']"
             :placeholder="translations['product_id']"
             :data="filters.productId.value"
/>

LabelInput.vue 文件:

<template>
    <div class="form-element">
        <label :for="inputId" :class="['form-element-title',  'js-focused': focused ]"> label </label>
        <input @input="updateValue($event.target.value)" :type="inputType" :id="inputId" :name="inputEnabled ? inputName : false" v-model="inputData" :placeholder="placeholder" @focus="focused = true" @blur="focused = false">
    </div>
</template>

<script lang="ts">
import  Component, Prop, Vue  from 'vue-property-decorator';

@Component
export default class LabelInput extends Vue 
    @Prop( default: () => '' ) inputId!: string
    @Prop( default: () => '' ) inputType!: string
    @Prop( default: () => '' ) inputName!: string
    @Prop( default: () => '' ) label!: string
    @Prop( default: () => '' ) placeholder!: string
    @Prop( default: () =>  ) data!: []
    @Prop( default: () => true ) inputEnabled!: string

    private inputData = this.data
    private focused = false

    updateValue (value: string) 
        this.$root.$emit('input', 'inputName' : this.inputName, 'inputValue' : value);
    

所以模型通过filters.productId.value作为数据传递,然后变成局部变量inputData。

现在,如果我更改输入值,我会使用 updateValue 函数,该函数会发出输入名称并将其值返回到父组件,从而更新原始 filters.productId.value 值。这很好用。

现在的问题是我的父组件上有一个函数,它将 filters.productId.value 值重置回 null。它看起来像这样:

clearFilters() 
    for (const [key, value] of Object.entries(this.filters)) 
        this.filters[key].value = null;
    

现在我的子组件 LabelInput 中的数据没有改变,即使 filters.productId.value 现在为空。如何刷新子组件?

【问题讨论】:

看来这个问题是由this.data 没有同步到this.inputData 父组件更改the props=data 引起的。所以一种解决方案是为this.data 添加一只手表,当它的值改变时,让this.inputData=this.data 在你的LabelInput.vue 但是 LabelInput 组件不知道数据何时更改。我怎么知道什么时候应该设置它? 【参考方案1】:

我还没有将 Vue 用作类组件,但似乎问题在于将 prop 设置为类中的属性。

private inputData = this.data

Vue 监听 prop 变化,但是 data 不监听引用的变化,所以 prop 发生了变化,但 inputData 没有变化,因为 Vue 没有为它设置监听器。

当我想做与您类似的事情时,我使用computed 属性。查看docs 看看它是如何实现的。

使用计算属性。

get inputData() 
  return this.data;

我相信这会如您所愿。

为了更好地可视化我们在 cmets 中所说的内容,下面是没有 v-model 的代码的样子。

输入:

<input :value="inputData" @input="updateValue($event.target.value)" :type="inputType" :id="inputId" :name="inputEnabled ? inputName : false" :placeholder="placeholder" @focus="focused = true" @blur="focused = false">

吸气剂:

get inputData() 
  // you can change your prop here and return a new value. 
  // A lower or upper case, for example. Just don't change the prop, only use it.
  return this.data;

更新值:

updateValue(value) 
  this.$root.$emit('input', 'inputName' : this.inputName, 'inputValue' : value);
    

另外,我认为不需要使用$root,因为您可以在组件中使用$emit。对于非类组件你通过this.$emit访问,我相信这里应该是一样的。

你可以做的另一个改变是在 html 中监听事件:

<label-input inputId="formProductId"
             inputType="number"
             inputName="productId"
             :inputEnabled="filters.productId.options"
             :label="translations['product_id']"
             :placeholder="translations['product_id']"
             :data="filters.productId.value"
             @updateValue="doSomething"

/>

您可以声明一个名为doSomething 的方法,它将接收到的值作为第一个参数,或者简单地将变量设置为事件@updateValue="(value) =&gt; filters.productId.value = value" 接收到的值。这也可以。

【讨论】:

很好,它现在看起来可以工作了。但是二传手呢?它应该是什么样子?我无法设置 this.data 它,因为我收到有关直接改变属性的警告。还是我应该将设置器留空? 您可以使用set inputData() // emit the event to parent component here 。 Here 描述得很好。 我看到您正在使用 v-model 和 @input,我认为没有必要同时使用它们。我相信您可以仅将 v-model 与 getter 和 setter 一起使用,并在 setter 内发出事件。 另一种选择是使用:value=inputData@input 事件而不使用v-model。这不会触发有关直接改变属性的警告。这就是我通常使用输入处理组件内部数据的方式,并且效果惊人。很清楚,子组件的工作只是接收用户输入的数据并将其发送给负责的(在本例中为父)组件。 是的,您需要使用 emit 两种方式将数据发送到父组件。您的输入应如下所示:&lt;input :value="inputData" @input="updateValue(...)"&gt;,您的方法:get inputName() return this.data updateValue(val) // emit here。这样,您的子组件从父组件接收数据并显示它,然后您的 updateValue 将其发送给父组件,父组件再次将值发送给子组件。这样孩子接收到的数据来显示它并且不会改变它。只有在父组件中需要设置filters.productId.value=value才能更改数据。

以上是关于Vue.js 更新的属性不会在子组件上更改的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js 中父级数据更改时如何重新渲染内部组件

Vue JS typescript 组件找不到注入实例属性

Vue.js - 如何将 prop 作为 json 数组传递并在子组件中正确使用?

在 Laravel 的前端 vue.js 上未更新组件的内容

如何使用 Vue JS 在子组件中保留数字和增量?

Vue.js 源码分析 ref属性详解