试图通过使用 $emit 来避免改变 3 级深度的道具
Trying to avoid mutating a prop that goes 3 levels deep by using $emit
编辑:Here's a repo我做了更容易的解析。
我有一个在数据table 中列出产品的组件。 table 的第一列是一个 link,它显示了一个带有被点击产品形式的模式(使用其 ID)。我将 PrimeVue 库用于样式和组件。
<template>
<Column field="id" headerStyle="width: 5%">
<template #body="slotProps">
<ProductForm :product="slotProps.data" :show="showModal(slotProps.data.id)" />
<a href="#" @click.stop="toggleModal(slotProps.data.id)">
<span class="pi pi-external-link"> </span>
</a>
</template>
</Column>
</template>
<script>
import ProductForm from "./forms/ProductForm";
export default {
data() {
return {
activeModal: 0,
}
},
components: { ProductForm },
methods: {
toggleModal: function (id) {
if (this.activeModal !== 0) {
this.activeModal = 0;
return false;
}
this.activeModal = id;
},
showModal: function (id) {
return this.activeModal === id;
},
},
</script>
模态框实际上是 ProductForm 组件的一个子组件(我制作了一个模态框模板,这样我就可以重用它)。所以它是 3 个组件(ProductList -> ProductForm -> BaseModal)。这是产品形式:
<template>
<div>
<BaseModal :show="show" :header="product.name">
<span class="p-float-label">
<InputText id="name" type="text" :value="product.name" />
<label for="name">Product</label>
</span>
</BaseModal>
</div>
</template>
<script>
import BaseModal from "../_modals/BaseModal";
export default {
props: ["product", "show"],
components: { BaseModal },
data() {
return {};
},
};
</script>
当模式弹出时,它使用 ProductForm 子组件。这是 BaseModal 组件:
<template>
<div>
<Dialog :header="header" :visible.sync="show" :modal="true" :closable="true" @hide="doit">
<slot />
</Dialog>
</div>
</template>
<script>
export default {
props: {
show: Boolean,
header: String,
},
methods: {
doit: function () {
let currentShow = this.show;
this.$emit("showModel", currentShow)
},
},
data() {
return {
};
},
};
</script>
我正在传递 product
对象和一个 show
布尔值,它指定模态是否可见,从第一个组件 (ProductList) 一直到 ProductForm 组件,然后最后是 BaseModal 组件。模态是 PrimeVue component called Dialog
。该组件实际上有自己的 属性,称为“closable”,它在单击时使用 X 按钮关闭模式,该按钮与名为 hide
的事件相关联。一切实际上都有效。我可以打开模式并关闭它。出于某种原因,我必须在初始后打开之前单击另一个模式 link 两次。
问题是当我关闭模式时,出现 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: "show"
错误。我已经尝试了一切以发出事件并更改那里的原始道具值,但错误仍然存在(即使来自上面的代码)但我不确定是否因为我有 3 个组件深度它不会工作。我对使用 props 和 slots 以及 $emit 还很陌生,所以我知道我做错了什么。我还不熟悉这么深的组件布局,所以我什至可能无法正确完成整个布局。我错过了什么?
好吧,你正在从 BaseModal
发出 showModel
事件,但你没有在 parent 上监听它并转发它+在 grandparent 上监听(ProductForm
)
但主要问题是 BaseModal
中的 :visible.sync="show"
。这与您执行 :visible="show" @update:visible="show = $event"
(docs) 相同。因此,当 Dialog
关闭时,PrimeVue 发出 update:visible
事件,该事件由 BaseModal
组件选择(感谢 .sync
修饰符)并导致 show
prop inside BaseModal
和错误信息...
记住 永远不要直接使用属性值 v-model
或 .sync
要修复它,请通过 computed with the setter:
间接使用道具
BaseModal
<template>
<div>
<Dialog :header="header" :visible.sync="computedVisible" :modal="true" :closable="true">
<slot />
</Dialog>
</div>
</template>
<script>
export default {
props: {
show: Boolean,
header: String,
},
computed: {
computedVisible: {
get() { return this.show },
set(value) { this.$emit('update:show', value) }
}
},
};
</script>
现在您可以将相同的计算添加到您的 ProductForm
组件中并将模板更改为 <BaseModal :show.sync="computedVisible" :header="product.name">
(因此当 ProductForm
接收到 update:show
事件时,它将发出与 parent 相同的事件 - 这是必需的,因为 Vue 事件不会像 DOM 事件那样“冒泡”,只有即时 parent 组件接收事件)
最后一步是处理 ProductList
中的 update:show
:
<ProductForm :product="slotProps.data" :show="showModal(slotProps.data.id)" @update:show="toggleModal(slotProps.data.id)"/>
编辑:Here's a repo我做了更容易的解析。
我有一个在数据table 中列出产品的组件。 table 的第一列是一个 link,它显示了一个带有被点击产品形式的模式(使用其 ID)。我将 PrimeVue 库用于样式和组件。
<template>
<Column field="id" headerStyle="width: 5%">
<template #body="slotProps">
<ProductForm :product="slotProps.data" :show="showModal(slotProps.data.id)" />
<a href="#" @click.stop="toggleModal(slotProps.data.id)">
<span class="pi pi-external-link"> </span>
</a>
</template>
</Column>
</template>
<script>
import ProductForm from "./forms/ProductForm";
export default {
data() {
return {
activeModal: 0,
}
},
components: { ProductForm },
methods: {
toggleModal: function (id) {
if (this.activeModal !== 0) {
this.activeModal = 0;
return false;
}
this.activeModal = id;
},
showModal: function (id) {
return this.activeModal === id;
},
},
</script>
模态框实际上是 ProductForm 组件的一个子组件(我制作了一个模态框模板,这样我就可以重用它)。所以它是 3 个组件(ProductList -> ProductForm -> BaseModal)。这是产品形式:
<template>
<div>
<BaseModal :show="show" :header="product.name">
<span class="p-float-label">
<InputText id="name" type="text" :value="product.name" />
<label for="name">Product</label>
</span>
</BaseModal>
</div>
</template>
<script>
import BaseModal from "../_modals/BaseModal";
export default {
props: ["product", "show"],
components: { BaseModal },
data() {
return {};
},
};
</script>
当模式弹出时,它使用 ProductForm 子组件。这是 BaseModal 组件:
<template>
<div>
<Dialog :header="header" :visible.sync="show" :modal="true" :closable="true" @hide="doit">
<slot />
</Dialog>
</div>
</template>
<script>
export default {
props: {
show: Boolean,
header: String,
},
methods: {
doit: function () {
let currentShow = this.show;
this.$emit("showModel", currentShow)
},
},
data() {
return {
};
},
};
</script>
我正在传递 product
对象和一个 show
布尔值,它指定模态是否可见,从第一个组件 (ProductList) 一直到 ProductForm 组件,然后最后是 BaseModal 组件。模态是 PrimeVue component called Dialog
。该组件实际上有自己的 属性,称为“closable”,它在单击时使用 X 按钮关闭模式,该按钮与名为 hide
的事件相关联。一切实际上都有效。我可以打开模式并关闭它。出于某种原因,我必须在初始后打开之前单击另一个模式 link 两次。
问题是当我关闭模式时,出现 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: "show"
错误。我已经尝试了一切以发出事件并更改那里的原始道具值,但错误仍然存在(即使来自上面的代码)但我不确定是否因为我有 3 个组件深度它不会工作。我对使用 props 和 slots 以及 $emit 还很陌生,所以我知道我做错了什么。我还不熟悉这么深的组件布局,所以我什至可能无法正确完成整个布局。我错过了什么?
好吧,你正在从 BaseModal
发出 showModel
事件,但你没有在 parent 上监听它并转发它+在 grandparent 上监听(ProductForm
)
但主要问题是 BaseModal
中的 :visible.sync="show"
。这与您执行 :visible="show" @update:visible="show = $event"
(docs) 相同。因此,当 Dialog
关闭时,PrimeVue 发出 update:visible
事件,该事件由 BaseModal
组件选择(感谢 .sync
修饰符)并导致 show
prop inside BaseModal
和错误信息...
记住 永远不要直接使用属性值 v-model
或 .sync
要修复它,请通过 computed with the setter:
间接使用道具BaseModal
<template>
<div>
<Dialog :header="header" :visible.sync="computedVisible" :modal="true" :closable="true">
<slot />
</Dialog>
</div>
</template>
<script>
export default {
props: {
show: Boolean,
header: String,
},
computed: {
computedVisible: {
get() { return this.show },
set(value) { this.$emit('update:show', value) }
}
},
};
</script>
现在您可以将相同的计算添加到您的 ProductForm
组件中并将模板更改为 <BaseModal :show.sync="computedVisible" :header="product.name">
(因此当 ProductForm
接收到 update:show
事件时,它将发出与 parent 相同的事件 - 这是必需的,因为 Vue 事件不会像 DOM 事件那样“冒泡”,只有即时 parent 组件接收事件)
最后一步是处理 ProductList
中的 update:show
:
<ProductForm :product="slotProps.data" :show="showModal(slotProps.data.id)" @update:show="toggleModal(slotProps.data.id)"/>