100 行代码实现 vue3 toast 组件
前言
日常移动端h5开发经常会显示一些简短的提示,方便告知用户一些不是非常重要的信息。例如当接口请求返回一些业务错误信息以及交互上面的提示信息。
很多ui库已经封装好了 Toast 组件,并且也有很多文章写了如何实现,这里分享下vue3 换种简洁的方式实现 toast 组件。
使用方式
直接调用 toast 方法,动态生成 toast弹框, 其中参数满足字符串和对象类型。
test/app.vue
<script lang="ts" setup>
import { toast } from '../lib/index';
const handleShow = () => {
// toast({
// message: `你好aaaa${Math.random()}`,
// placement: 'top',
// duration: 3000,
// });
toast(`${Math.random()}`);
};
</script>
<template>
<div class="app">
<button @click="handleShow">弹出toast</button>
</div>
</template>
<style>
* {
margin: 0;
padding: 0;
}
</style>
示例
分析
- 提供动态生成 toast 的方法, 参数满足字符串以及对象类型。
- 将 vmdom 渲染到具体节点并且增加响应控制。
最小化实现
lib/toast/index.ts
import { h, ref, render, effect } from 'vue';
import Toast from './index.vue';
let timeId;
// 抽离不同平台 实现 具体 api
export function baseToast(opts, { mountEl }) {
if (!opts) return;
let defaultOpts = {
message: '',
placement: 'top',
duration: 3000,
};
if (typeof opts === 'string') {
defaultOpts.message = opts;
}
if (typeof opts === 'object') {
defaultOpts = {
...defaultOpts,
...opts,
};
}
mountEl && mountEl(defaultOpts);
}
// web 平台
export function toast(opts) {
return baseToast(opts, {
mountEl(a) {
if (timeId) {
clearTimeout(timeId);
timeId = undefined;
}
const t = ref(true);
effect(() => {
render(
h(Toast, {
show: t.value,
message: a.message,
placement: a.placement,
}),
document.body
);
});
timeId = setTimeout(() => {
t.value = false;
}, a.duration);
},
});
}
lib/toast/index.vue
<script lang="ts" setup>
const props = defineProps<{
show: boolean;
message: string;
placement: 'top' | 'center';
}>();
</script>
<template>
<transition name="fade">
<div v-if="props.show" :class="['toast', placement]">
{{ props.message }}
</div>
</transition>
</template>
<style scoped>
/* vue动画过渡效果设置 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.toast {
position: fixed;
padding: 10px 8px;
left: 50%;
background-color: black;
color: #fff;
border-radius: 8px;
}
.toast.top {
top: 50px;
transform: translateX(-50%);
}
.toast.center {
top: 50%;
transform: translate(-50%, -50%);
}
</style>
思考
如果通过js动态生成,最终都绕不开dom api 的操作,也就是命令式编程。原生操作最简单暴力,主要是维护起来不方便。
const el = document.createElement('div');
el.innerHTML = a.message;
el.setAttribute('class', 'toast');
document.body.appendChild(el);
setTimeout(() => {
document.body.removeChild(el);
}, a.duration);