事件总线 和 插槽(slot )的介绍
提示:以下是本篇文章正文内容,下面案例可供参考
一、事件总线
可以通过事件总线实现组件之间的通信。
配置全局属性(定义 $bus
为全局属性名,之后可以通过 this.$bus
可以调用 )
app.config.globalProperties.$bus = mitt();
app.config.globalProperties.$inEventName = '输入';
app.mount('#first');
注册事件监听器,要明确 事件事件 和 监听器。
created() {
// 注册事件监听器
// 需要明确 事件名称 和 事件监听器
this.$bus.on( this.$inEventName, this.changeContent );
}
1. 手动实现事件监听和触发
尝试获得指定事件名称对应的监听器数组,成功获取到监听器数组,并且在监听器数组末尾追加新的监听器;如果未能获取到监听器数组,则向 map 中添加新的映射。
on( name, listener ) {
// 先尝试获得指定事件名称对应的监听器数组
let listeners = this.#map.get(name);
// 若成功获取到监听器数组
if( listeners ) {
// 则在监听器数组末尾追加新的监听器
listeners.push(listener);
return;
}
// 若未能获取到监听器数组,则向 map 中添加新的映射
this.#map.set( name, [listener] );
}
尝试获得指定事件名称对应的监听器数组。
emit(name, payload){
// 先尝试获得指定事件名称对应的监听器数组
let listeners = this.#map.get(name);
if( listeners ){
listeners.forEach( fn => fn(payload) );
}
}
off(name) {
if( this.#map.has( name ) ) {
this.#map.delete(name);
}
}
toString() {
return this.#map.entries();
}
2.模板引用
挂载结束后引用都会被暴露在 this.$refs
之上:
mounted() {
console.log( this.$refs );
console.log( this.$refs.unameInput );
}
注意,你只可以在组件挂载后才能访问模板引用。如果你想在模板中的表达式上访问 $refs.input
,在初次渲染时会是 null
。这是因为在初次渲染前这个元素还不存在呢!
二、插槽(slot )
把父组件的内容插入到子组件预留好的位置上。
1.插槽出口(slot outlet)和插槽内容(slot content)
子组件预留的位置为 插槽出口(slot outlet) ,而父组件要插入的内容称为 插槽内容(slot content)。
在子元素中设定标签 <slot>
作为 插槽出口(slot outlet)。
template: `<button @click="handleClick">
<slot></slot>
</button>`,
而在父元素中在标签 <FancyButton>
template: `<div class="wrapper">
<FancyButton>点我试试</FancyButton>
<FancyButton>{{this.text}}</FancyButton>
</div>`,
<slot>
元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。通过使用插槽,<FancyButton>
仅负责渲染外层的 <button>
(以及相应的样式),而其内部的内容由父组件提供。
默认内容
在外部没有提供任何内容的情况下,可以为插槽指定默认内容。
template: `<button @click="handleClick">
<slot>
默认内容
</slot>
</button>`,
template: `<button @click="handleClick">
<slot>
默认内容
</slot>
</button>`,
如果我们想在父组件没有提供任何插槽内容时在 <button>
内渲染“Submit”,只需要将“Submit”写在 <slot>
标签之间来作为默认内容:
<button type="submit">
<slot>
Submit <!-- 默认内容 -->
</slot>
</button>
当我们在父组件中使用 <SubmitButton>
且没有提供任何插槽内容时:
<SubmitButton />
“Submit”将会被作为默认内容渲染:
<button type="submit">Submit</button>
2.具名插槽
<slot>
元素可以有一个特殊的 attribute name
,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容:
template: `<div class="base-layout">
<header>
<slot name="header">我是头</slot>
</header>
<main>
<slot>我是中间的</slot>
</main>
<footer>
<slot name="footer">我是脚</slot>
</footer>
</div>`,
这类带 name
的插槽被称为具名插槽 (named slots)。没有提供 name
的 <slot>
出口会隐式地命名为“default”。
在父组件中使用 <BaseLayout>
时,我们需要一种方式将多个插槽内容传入到各自目标插槽的出口。此时就需要用到具名插槽了:
要为具名插槽传入内容,我们需要使用一个含 v-slot
指令的 <template>
元素,并将目标插槽的名字传给该指令:
<BaseLayout>
<template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
</BaseLayout>
v-slot
有对应的简写 #
,因此 <template v-slot:header>
可以简写为 <template #header>
。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。
3.作用域插槽
template: `<div class="base-layout">
<header>
<slot name="top" message="顶部">头</slot>
</header>
<div>
<aside>
<slot name="left" message="中左">中左</slot>
</aside>
<main>
<slot name="default" message="中右">中右</slot>
</main>
</div>
<footer>
<slot name="bottom" message="底部">脚</slot>
</footer>
</div>`
template: `<BaseLayout>
<template #top="sp">
这里是{{sp.message}}
</template>
<template #left="sp">
这里是{{sp.message}}
</template>
<template #default="sp">
这里是{{sp.message}}
</template>
<template #bottom="sp">
这里是{{sp.message}}
</template>
</BaseLayout>`