1、index.vue
一般toast会有如下功能:背景色、字体颜色、文本、停留时间
<template>
<div class="toast-box" >
<p class="toast-value" :style="{background: background, color: color}">
{{ value }}
</p>
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Toast',
props: {
value: {
type: String,
default: ''
},
duration: {
type: Number,
default: 3000
},
background: {
type: String,
default: '#000'
},
color: {
type: String,
default: '#fff'
}
}
})
</script>
<style>
.toast-box {
position: fixed;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.toast-value {
max-width: 100px;
background: rgb(8, 8, 8);
padding: 8px 10px;
border-radius: 4px;
text-align: center;
display: inline-block;
animation: anim 0.5s;
}
@keyframes anim {
0% {opacity: 0;}
100%{opacity:1;
}
}
.toast-value.remove{
animation: remove 0.5s;
}
@keyframes remove {
0% {opacity: 1;}
100%{opacity:0;
}
}
</style>
2、index.js 导出Toast方法
创建
首先使用createVNode
方法创建一个vNode独享
使用render
方法转换成真实dom
添加到body
上
销毁
首先添加一个淡入淡出效果
使用render
将真实设置为null
移除创建的dom
以下代码为TS写法,不支持部分去掉代码即可
import { createVNode, render } from 'vue'
import toastTemplate from './index.vue'
export interface IProps {
value?: string;
duration?: number;
background?: string;
color?: string;
}
const defaultOpt = { // 创建默认参数
duration: 3000
}
export interface ResultParams {
destory?: () => void;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
const Toast = (options: IProps):ResultParams => {
const container = document.createElement('div')
const opt = {...defaultOpt,...options}
const vm = createVNode(toastTemplate, opt) // 创建vNode
render(vm, container)
document.body.appendChild(container) // 添加到body上
const destory = ()=> {
const dom = vm.el as HTMLDivElement
if(dom.querySelector('.toast-value')) {
dom.querySelector('.toast-value')?.classList.add('remove') // 销毁时添加淡入淡出效果
const t = setTimeout(() => { // 淡入淡出效果之后删除dom节点
render(null, container)
document.body.removeChild(container)
clearTimeout(t)
},500);
}
}
if(opt.duration) { // 如果传入的值为0可以持续保留在页面,需要手动销毁
const timer = setTimeout(()=> {
destory()
clearTimeout(timer)
}, opt.duration)
}
return {
destory
}
}
export default Toast
3、main.js插件全局引入
import Toast from '@/components/Toast/index'
app.config.globalProperties.$toast = Toast;
// app.use(Toast) 用use来全局载入会导致我们不能使用this的地方不太好调用。
4、使用
this.$toast({
value: 'toast',
duration: 0, // 如果大于0则不必使用destory方法
background: '#000',
color: '#fff'
})
setTimeout(() => {
this.$toast.destory && this.$toast.destory()
}, 2000)
setup内使用
import { getCurrentInstance} from 'vue'
setup() {
const { proxy } = getCurrentInstance();
const showToastEvent = () => {
proxy.$toast({
value: 'toast',
duration: 1000,
background: '#000',
color: '#fff'
})
}
return {
showToastEvent,
}
}
<template>
<div class="loading">
加载中...
</div>
</template>
<script>
export default {
name: "loading",
}
</script>
<style scoped>
.loading {
position: fixed;
left: 50%;
top: 50%;
background-color: rgba(0, 0, 0, 0.2);
color: white;
transform: translate(-50%, -50%);
border-radius: 4px;
padding: 8px 10px;
z-index: 1000;
}
</style>
import { createApp } from "vue"
import Loading from './loading.vue'
export default {
instance: null,
parent: null,
times: 0,
// 为了保证多个同时loading的时候,只显示一个,并且需要全部close之后才消失
open() {
if (this.times == 0) {
this.instance = createApp(Loading)
this.parent = document.createElement("div")
let appDom = document.getElementById('app')
appDom.appendChild(this.parent)
this.instance.mount(this.parent)
}
this.times ++
},
close() {
this.times --
if (this.times <= 0) {
this.times = 0
let appDom = document.getElementById('app')
this.instance.unmount(this.parent)
appDom.removeChild(this.parent)
}
}
};
import loading from '@/components/loading/index'
app.config.globalProperties.$loading = loading;