点:
- 函数组件:指像ElMessage一样不用在template中渲染,可以直接在script中调用的组件
- Vite:import.meta.glob懒加载导入模块 https://cn.vitejs.dev/guide/features.html#glob-import
- 一丢丢JS:createElement创建元素,appendChild将元素插入到body中,removeChild移除元素
- 二丢丢JS:Object.keys https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
- Vue3创建实例:createApp() https://cn.vuejs.org/api/application.html#createapp
- Vue3全局变量:app.globalProperties https://cn.vuejs.org/api/application.html#app-config-globalproperties
步骤:
- 将需要做成函数组件的文件们放一个文件夹components的function中方便管理
- 正常写先将样式和功能完善,将来再将参数提出来
- function中创建index.ts文件,默认抛出一个方法:导入需要用到的模块,集中设置成全局变量
- main.ts中将刚刚的方法引入,app.use()即可
我的文件层级
NjxToast.vue代码 ↓
这里代码有一点点点点乱。。想要动画。。需要在接收到关闭的信号儿后延迟remove执行的时间。。。。。等以后想到更好的解决办法再来改
<script lang='ts' setup>
const props = defineProps({
// 实例信号
show: {
type: Boolean,
default: false
},
// 销毁实例
remove: {
type: Function,
default: () => { }
},
// 组件默认值
hasWrap: {
type: Boolean,
default: true
},
title: {
require: true,
type: String
},
continue: {
type: Number,
default: 2000
}
})
watch(() => props.show, (value) => {
if (value) {
nextTick(() => {
visible.value = true;
start();
})
}
}, { immediate: true })
const visible = ref(false);
const timer1 = shallowRef(0);
const timer2 = shallowRef(0);
const start = () => {
timer1.value = setTimeout(() => {
visible.value = false;
// for transition:destroy instance
timer2.value = setTimeout(() => {
clearTimer();
props.remove();
}, 500)
}, props.continue)
}
const clearTimer = () => {
timer1.value = 0;
timer2.value = 0;
}
onUnmounted(() => {
clearTimer();
})
</script>
<template>
<Transition>
<div v-if="visible">
<div class="toast-wrap" v-if="props.hasWrap && visible"></div>
<div class="toast-view" v-if="visible">{{ props.title }}</div>
</div>
</Transition>
</template>
<style lang="scss" scoped>
.toast-wrap {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background: transparent;
}
.toast-view {
display: inline-block;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 6px 12px;
background: rgba(0, 0, 0, .6);
border-radius: 4px;
color: #fff;
font-size: 12px;
}
</style>
NjxToast.ts代码 ↓
import { createApp } from 'vue'
import Toast from './NjxToast.vue'
export default function NjxToast(options: object) {
// 创建节点
const mountNode: HTMLDivElement = document.createElement('div');
// 插入body
document.body.appendChild(mountNode);
// 创建实例
const app = createApp(Toast, {
show: true,
remove: () => {
app.unmount() // 销毁实例
document.body.removeChild(mountNode)
},
...options
})
return app.mount(mountNode);
}
function里index.ts代码 ↓
import { App } from 'vue'
// 原先的globalEadger已弃用;使用glob懒加载导入模块
const fileList = import.meta.glob("@/components/function/**");
export default function (app: App) {
Object.keys(fileList).forEach(async (key) => {
// 筛选出ts后缀
if (key.split(".")[1] === "ts") {
const moduleS: any = await fileList[key]();
setGlobalProperties(app, moduleS);
}
});
}
// 将解析出的文件以全局变量的形式输出成函数组件
const setGlobalProperties = (app: App, modules: object) => {
Object.keys(modules).forEach(key2 => {
const moduleO = modules[key2];
app.config.globalProperties[`$${moduleO.name}`] =
moduleO;
})
}
使用页面代码 ↓ script中昂~别的配置项跟着title写就成
proxy.$NjxToast({
title: '日历提醒~~~~'
});