Vue child 属性不会在 parent 中的绑定值更改时更新

Vue child prop is not updated on bound value change in parent

我正在使用 vue-property-decorator 库。我有 2 个组件:parentchild, child 是一个有属性的组件 'modalHeader':

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

@Component({
    name: "modal"
})
export default class Modal extends Vue {
    @Prop({default: "Header"}) public modalHeader!: string;
}
</script>

Child 组件的模板:

<template>
    <div
        class="modal fade"
        id="detailsModal"
        tabindex="-1"
        role="dialog"
        aria-labelledby="detailsModalLabel"
        aria-hidden="true"
    >
        <div class="modal-dialog modal-lg" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="detailsModalLabel">
                        {{ modalHeader }}
                    </h5>
                </div>
                ...
                </div>
            </div>
        </div>
    </div>
</template>

我有一个 属性 'buttonPressed' 在我的 parent 组件,我想将它的属性绑定到 child 道具,因此每次根据按下的按钮我都会得到不同的 header 模态:

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import Modal from '../modal/Modal.vue';

interface ControlButtonSetting {
    id: string,
    name: string,
    enabled: boolean,
    isDanger: boolean,
    okText?: string,
    componentType?: IModalComponent,
}

@Component({
    name: "admin-dashboards-page",
    components: {
        "modal": Modal
    }
})
export default class AdminDashboardsPage extends Vue {

    private buttonsArray: ControlButtonSetting[] = [
        { id: "removeButton", name: "Remove", enabled: false, isDanger: true, okText: "Remove" }
    ];

    public buttonPressed!: ControlButtonSetting;

    private showModal = false;

    created(): void {
        this.buttonPressed = this.buttonsArray[0];
    }

    public controlButtonClicked(button: ControlButtonSetting): void {
        this.buttonPressed = button;
        this.showModal = true;
    }
}
</script>

Parent 组件的模板:

<template>
    <div>
        ...

        <!-- Modal -->
        <modal
            :modal-header.sync="buttonPressed.name"
            <template v-slot:modalBody>
                <component
                    :is="displayComponent()"
                    :dashboard-id="selectedListItemId">
                </component>
            </template>
        </modal>
    </div>
</template>

然而,'modalHeader'值只在页面加载时设置一次,但当'selectedButton' object 已更改,'modalHeader' 值未在 child 组件上更新。

对我有帮助的是将parent的object定义为Prop() :

@Prop() public buttonPressed!: ControlButtonSetting;

这样,每当 parent 的 prop 更新时,child 组件的 prop 也会更新。但是我开始在控制台中看到这条消息:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "buttonPressed"

我不太确定这里出了什么问题。你能帮我吗?

P.S。我的一种猜测是,我不是在 parent 中改变绑定 属性,而是用新的 object 替换它,这就是 parent-child 绑定可能不起作用的原因。去看看吧。

简而言之:您的 buttonPressed 数据不是反应性的,因为它没有被初始化。必须正确初始化 Vue 才能将其标记为反应式。

public buttonPressed!: ControlButtonSetting; // not initialised

您有两个选项可以正确初始化它:

#1 你在声明后直接给它一个合适的默认值,即使是空的。

public buttonPressed: ControlButtonSetting = {
  id: "",
  name: "",
  enabled: false,
  isDanger: false,
  okText: ""
}

#2 如果你想让它的默认值依赖于其他数据/道具,使用 data 工厂。 (不要为此使用 created 钩子)

public buttonPressed!: ControlButtonSetting; // use !: to tell typescript it's declared but in another place

// Even using `vue-property-decorator`, this will work and merge both data() and class properties
data () {
  return {
     buttonPressed: this.buttonsArray[0],
  }
}

不要将 buttonPressed 标记为道具,因为它不是。并且 Vue prop 不能从子对象更新,它会破坏父对象的反应性。


注意:Vue 道具应该始终提供一个类型。即使使用vue-property-decorator,你也必须指定它。

@Prop({ type: String, default: 'Header' }) <- Vue prop type, only for runtime
readonly modalHeader!: string // <- typescript type, only for build time

(我建议你使用 readonly 关键字来告诉 Typescript 这个 属性 不能更新,因为 props 在 Vue 中是不可变的)