Vue child 属性不会在 parent 中的绑定值更改时更新
Vue child prop is not updated on bound value change in parent
我正在使用 vue-property-decorator 库。我有 2 个组件:parent 和 child, 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 中是不可变的)
我正在使用 vue-property-decorator 库。我有 2 个组件:parent 和 child, 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 中是不可变的)