Vue:以编程方式修改开槽组件的道具
Vue: modify props of slotted component programmatically
问题
如何在运行时修改开槽元素的 prop
?
我是否使用了错误的生命周期方法?
是否也需要更改 slottedElement.type.props
?
例子
A.vue
<template>
<B key="tag-outer">
<B key="tag-inner" />
</B>
</template>
<script>
import B from "./B.vue";
export default {
name: "A",
components: {
B,
},
};
</script>
B.vue
<template>
<p>{{ mode }}</p>
<slot />
</template>
<script>
export default {
name: "B",
props: {
mode: {
type: String,
default: "outer",
},
key: String,
},
computed: {
hasSlot() {
return !!this.$slots.default;
},
},
beforeMount() {
this.modifySlottedElements();
},
beforeUpdate() {
this.modifySlottedElements();
},
methods: {
modifySlottedElements() {
if (this.hasSlot) {
this.$slots.default().forEach((slottedElement) => {
if (slottedElement.type.name === "B") {
// prevent concurrency issues
const copiedSlottedElement = JSON.parse(JSON.stringify(slottedElement));
console.log("before");
console.log(copiedSlottedElement);
slottedElement.props.mode = "inner";
console.log("modified");
console.log(slottedElement);
}
});
}
},
},
};
</script>
输出
before
type = Object {name: "B", props: Object, computed: Object, methods: Object, __file: "C:/dev/git/csx-vue/src/demo/test/B.vue", ...}
props = Object {key: "tag-inner"}
...
modified
type = Object {name: "B", props: Object, computed: Object, beforeMount: Function, beforeUpdate: Function, ...}
props = Object {key: "tag-inner", mode: "inner"}
...
已渲染
<p>outer</p>
<p>outer</p>
谢谢
解决方法
使用反向逻辑(检查父级)
<template>
<p>{{ modeProxy }}</p>
<slot />
</template>
<script>
export default {
name: "B",
props: {
mode: {
type: String,
default: "outer",
},
key: String,
},
data() {
return {
modeProxy: this.mode,
};
},
beforeMount() {
this.markSubMenu();
},
beforeUpdate() {
this.markSubMenu();
},
methods: {
markSubMenu() {
if (this.$parent.$.type.name === "B") {
this.modeProxy = "inner";
}
},
},
};
</script>
解决问题的最简单方法是使用 inject/provide
- 您的菜单组件将使用
inject
从父级(如果有的话)获取它的级别
- 和
provide
为它的子组件提供有关级别的信息
<template>
<div>
Level: {{ menuLevel }} / {{ menuLevel2 }}
<slot></slot>
</div>
</template>
<script>
import { provide, inject } from 'vue';
export default {
name: 'NestedMenu',
setup() {
// using Composition API
let menuLevel = inject('menuLevel', 1 /* this is default value */);
provide('menuLevel', menuLevel + 1);
return { menuLevel };
},
// Same functionality as above but using Options API
inject: {
menuLevel2: { default: 1 },
},
provide() {
return {
menuLevel2: this.menuLevel2 + 1,
};
},
};
</script>
问题
如何在运行时修改开槽元素的 prop
?
我是否使用了错误的生命周期方法?
是否也需要更改 slottedElement.type.props
?
例子
A.vue
<template>
<B key="tag-outer">
<B key="tag-inner" />
</B>
</template>
<script>
import B from "./B.vue";
export default {
name: "A",
components: {
B,
},
};
</script>
B.vue
<template>
<p>{{ mode }}</p>
<slot />
</template>
<script>
export default {
name: "B",
props: {
mode: {
type: String,
default: "outer",
},
key: String,
},
computed: {
hasSlot() {
return !!this.$slots.default;
},
},
beforeMount() {
this.modifySlottedElements();
},
beforeUpdate() {
this.modifySlottedElements();
},
methods: {
modifySlottedElements() {
if (this.hasSlot) {
this.$slots.default().forEach((slottedElement) => {
if (slottedElement.type.name === "B") {
// prevent concurrency issues
const copiedSlottedElement = JSON.parse(JSON.stringify(slottedElement));
console.log("before");
console.log(copiedSlottedElement);
slottedElement.props.mode = "inner";
console.log("modified");
console.log(slottedElement);
}
});
}
},
},
};
</script>
输出
before
type = Object {name: "B", props: Object, computed: Object, methods: Object, __file: "C:/dev/git/csx-vue/src/demo/test/B.vue", ...}
props = Object {key: "tag-inner"}
...
modified
type = Object {name: "B", props: Object, computed: Object, beforeMount: Function, beforeUpdate: Function, ...}
props = Object {key: "tag-inner", mode: "inner"}
...
已渲染
<p>outer</p>
<p>outer</p>
谢谢
解决方法
使用反向逻辑(检查父级)
<template>
<p>{{ modeProxy }}</p>
<slot />
</template>
<script>
export default {
name: "B",
props: {
mode: {
type: String,
default: "outer",
},
key: String,
},
data() {
return {
modeProxy: this.mode,
};
},
beforeMount() {
this.markSubMenu();
},
beforeUpdate() {
this.markSubMenu();
},
methods: {
markSubMenu() {
if (this.$parent.$.type.name === "B") {
this.modeProxy = "inner";
}
},
},
};
</script>
解决问题的最简单方法是使用 inject/provide
- 您的菜单组件将使用
inject
从父级(如果有的话)获取它的级别 - 和
provide
为它的子组件提供有关级别的信息
<template>
<div>
Level: {{ menuLevel }} / {{ menuLevel2 }}
<slot></slot>
</div>
</template>
<script>
import { provide, inject } from 'vue';
export default {
name: 'NestedMenu',
setup() {
// using Composition API
let menuLevel = inject('menuLevel', 1 /* this is default value */);
provide('menuLevel', menuLevel + 1);
return { menuLevel };
},
// Same functionality as above but using Options API
inject: {
menuLevel2: { default: 1 },
},
provide() {
return {
menuLevel2: this.menuLevel2 + 1,
};
},
};
</script>