封装 vuetify 的 snackbar 组件

前言

封装 vuetify 的 snackbar 组件。

在一些组件库中,提供了一种命令的方式来让我们调用提示框,比如:

在 Vuetify 中,并没有这样的现成的接口供我们使用,但 Vuetify 提供了一个 Snackbar 组件,用来显示信息。

所以本篇文章会告诉大家如何来封装一个 Snackbar ,使得我们能够以命令的方式来调用 Snackbar 。

正文

准备

在实现之前,我们要明白给到用户侧的接口是什么,这次我们主要模仿的是 NaiveUI 的实现,也就是说我们要提供一个 Provider 组件,以及一个 composition 函数。

Provider 组件主要用来集中管理所有的 Snackbar 实例,而 useMessage 函数则是用来获取暴露的接口,用来实例化新的 Snackbar 。

我们可以暂定 useMessage 主要返回一个函数,它的声明看起来如下:

1
2
3
type MessageReturnType = (msg: string) => void;

declare function useMessage(): MessageReturnType;

使用的时候看起来是下面这样的:

1
2
3
4
5
6
7
8
9
10
11
<script setup>
import { useMessage } from "xxx";

const message = useMessage();

const showMessage = () => {
message("hello world!");
}
</script>

<!-- 其他代码 -->

对于 Provider ,它用于包裹在顶层的组件中,提供一个渲染 Snackbar 的容器:

1
2
3
4
5
6
7
8
9
10
<!-- app.vue -->
<script setup>
import { Provider } from "xxx";
</script>

<template>
<Provider>
<OtherComponent></OtherComponent>
</Provider>
</template>

Provider

对于 Provider ,我们需要做两件事,一个是根据响应式对象来渲染 Snackbar ,一个是通过 provide 接口来改变内部的响应式对象,从而动态的生成 Snackbar

我们先定义一个响应式的数组,这个数组只存需要显示的 message ,对应我们调用时的参数

1
2
3
4
5
6
7
<script setup lang="ts">
// ...

const instanceList = ref<Array<{ id: number; msg: string; value: boolean }>>(
[]
);
</script>

接着我们根据这个数组来实例化 Snackbar

1
2
3
4
5
6
7
8
9
10
<template>
<slot></slot>
<v-snackbar
v-for="inst of instanceList"
:key="inst.id"
v-model:model-value="inst.value"
>
{{ inst.msg }}
</v-snackbar>
</template>

接着我们需要暴露接口来更新这个 instanceList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<script setup lang="ts">
// ...

let id = 0;
const message = (msg: string) => {
instanceList.value.push({
id: ++id,
msg,
value: true,
});
};

provide("messageFn", message);
</script>

useMessage

在完成 Provider 之后,我们编写一个 composition ,它的内部很简单,就是获取 Provider 组件 provide 的一个函数,然后返回

1
2
3
4
const useMessage = () => {
const message = inject<(msg: string) => void>("messageFn")!;
return message;
};

至此,我们的核心代码基本上就写完了,当然,它的功能还很简陋,不过效果是符合预期的

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script setup lang="ts">
import { useMessage } from "xxx";

const message = useMessage();

const showMessage = () => {
message("hello world!");
};
</script>

<template>
<div class="w-screen h-screen d-flex align-center justify-center">
<v-btn @click="showMessage">show</v-btn>
</div>
</template>

测试效果如下:

后记

通过这种方式,可以编写一个丰富的 message 接口。

我已经在 github 上开源了一个项目, vuetify-message-vue3 ,它的底层就基于本文的实现方式

而且它支持更加丰富的参数,更加人性化的调用,以及完善的类型提示。

如果你喜欢这个项目,可以点个 star ~ 希望它可以帮你更快地写出业务代码。