Vue 3 从插槽访问子组件
Posted
技术标签:
【中文标题】Vue 3 从插槽访问子组件【英文标题】:Vue 3 access child component from slots 【发布时间】:2021-02-01 01:56:51 【问题描述】:我目前正在进行自定义验证,如果可能,我希望访问子组件并在其中调用一个方法。
表单包装器
<template>
<form @submit.prevent="handleSubmit">
<slot></slot>
</form>
</template>
<script lang="ts">
import defineComponent from 'vue';
export default defineComponent(
setup(props, slots )
const validate = (): boolean =>
if (slots.default)
slots.default().forEach((vNode) =>
if (vNode.props && vNode.props.rules)
if (vNode.component)
vNode.component.emit('validate');
);
return false;
;
const handleSubmit = (ev: any): void =>
validate();
;
return
handleSubmit,
;
,
);
</script>
当我调用slot.default()
时,我会得到正确的子组件列表并且可以看到它们的道具。但是,vNode.component
始终是 null
我的代码基于example,但它适用于 vue 2。
如果有人可以帮助我,那就太好了,或者这甚至可能做到。
【问题讨论】:
【参考方案1】:在您提供的链接中,Linus 提到使用 $on
和 $off
来执行此操作。这些已在 Vue 3 中删除,但您可以使用 recommended mitt
库。
一种方法是将submit
事件分派给子组件,并让它们在收到submit
时发出validate
事件。但也许您无权将此添加到子组件?
JSFiddle Example
<div id="app">
<form-component>
<one></one>
<two></two>
<three></three>
</form-component>
</div>
const emitter = mitt();
const ChildComponent =
setup(props, emit )
emitter.on('submit', () =>
console.log('Child submit event handler!');
if (props && props.rules)
emit('validate');
);
,
;
function makeChild(name)
return
...ChildComponent,
template: `<input value="$name" />`,
;
const formComponent =
template: `
<form @submit.prevent="handleSubmit">
<slot></slot>
<button type="submit">Submit</button>
</form>
`,
setup()
const handleSubmit = () => emitter.emit('submit');
return handleSubmit ;
,
;
const app = Vue.createApp(
components:
formComponent,
one: makeChild('one'),
two: makeChild('two'),
three: makeChild('three'),
);
app.mount('#app');
【讨论】:
【参考方案2】:我找到了另一个解决方案,灵感来自 quasar 框架。
-
表单组件提供()绑定和取消绑定功能。
bind() 将验证函数推送到数组并存储在 Form 组件中。
输入组件从父表单组件注入绑定和取消绑定功能。
使用 self validate() 函数和 uid 运行 bind()
表单从提交按钮监听提交事件。
遍历所有这些 validate() 数组,如果没有问题则 emit('submit')
表单组件
import
defineComponent,
onBeforeUnmount,
onMounted,
reactive,
toRefs,
provide
from "vue";
export default defineComponent(
name: "Form",
emits: ["submit"],
setup(props, emit )
const state = reactive(
validateComponents: []
);
provide("form",
bind,
unbind
);
onMounted(() =>
state.form.addEventListener("submit", onSubmit);
);
onBeforeUnmount(() =>
state.form.removeEventListener("submit", onSubmit);
);
function bind(component)
state.validateComponents.push(component);
function unbind(uid)
const index = state.validateComponents.findIndex(c => c.uid === uid);
if (index > -1)
state.validateComponents.splice(index, 1);
function validate()
let valid = true;
for (const component of state.validateComponents)
const result = component.validate();
if (!result)
valid = false;
return valid;
function onSubmit()
const valid = validate();
if (valid)
emit("submit");
);
输入组件
import defineComponent from "vue";
export default defineComponent(
name: "Input",
props:
rules:
default: () => [],
type: Array
,
modelValue:
default: null,
type: String
setup(props)
const form = inject("form");
const uid = getCurrentInstance().uid;
onMounted(() =>
form.bind( validate, uid );
);
onBeforeUnmount(() =>
form.unbind(uid);
);
function validate()
// validate logic here
let result = true;
props.rules.forEach(rule =>
const value = rule(props.modelValue);
if(!value) result = value;
)
return result;
);
用法
<template>
<form @submit="onSubmit">
<!-- rules function -->
<input :rules="[(v) => true]">
<button label="submit form" type="submit">
</form>
</template>
【讨论】:
以上是关于Vue 3 从插槽访问子组件的主要内容,如果未能解决你的问题,请参考以下文章