前言
一个页面有多个按钮,每个按钮都要添加loading效果,高级前端是如何在Vue3控制按钮是否显示loading效果的呢?
普通前端
我们先来看看初级普通前端平常是怎么给按钮添加loading效果的:
xml
<script setup >
import { ref } from 'vue'
const asyncFn = () => new Promise(resolve => {
setTimeout(resolve, 3000)
})
const loading1 = ref(false)
const handler1 = async () => {
loading1.value = true
try {
await asyncFn()
} finally {
loading1.value = false
}
}
const loading2 = ref(false)
const handler2 = async () => {
loading2.value = true
try {
await asyncFn()
} finally {
loading2.value = false
}
}
const loading3 = ref(false)
const handler3 = async () => {
loading3.value = true
try {
await asyncFn()
} finally {
loading3.value = false
}
}
</script>
<template>
<el-button type="primary" @click="handler1" :loading="loading1">
按钮1
</el-button>
<el-button @click="handler2" :loading="loading2">
按钮2
</el-button>
<el-button type="primary" plain @click="handler3" :loading="loading3">
按钮3
</el-button>
</template>
通过以上代码可以看到,一个页面有多个按钮,每个按钮都要添加loading效果,所以声明了loading1、loading2、loading3 ...变量来控制按钮是否显示loading效果,非常不优雅。 那么高级前端是如何优雅的给按钮添加loading效果的呢?
高级前端
首先先封装一个MyButton
组件:
xml
<script setup >
import { ref, useSlots } from 'vue'
const props = defineProps(['onClick'])
const loading = ref(false)
const clickHandler = async (e) => {
loading.value = true
try {
await props.onClick(e)
} finally {
loading.value = false
}
}
const slots = useSlots()
</script>
<template>
<el-button @click="clickHandler" :loading="loading">
<template v-for="(_, key, i) in slots" :key="i" #[key]>
<slot :name="key" />
</template>
</el-button>
</template>
接下来引用MyButton
组件,绑定click
事件,返回Promise
就可以优雅的给按钮添加一个loading效果了
xml
<script setup >
import MyButton from './MyButton.vue';
const asyncFn = () => new Promise(resolve => {
setTimeout(resolve, 3000)
})
const handler1 = async () => {
// ...
await asyncFn()
}
const handler3 = () => {
// ...
return asyncFn()
}
</script>
<template>
<MyButton type="primary" @click="handler1">
按钮1
</MyButton>
<MyButton @click="asyncFn">
按钮2
</MyButton>
<MyButton type="primary" plain @click="handler3">
<template #loading>
<div class="custom-loading">
<svg class="circular" viewBox="-10, -10, 50, 50">
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)" />
</svg>
</div>
</template>
按钮3
</MyButton>
</template>
<style scoped>
.el-button .custom-loading .circular {
margin-right: 6px;
width: 18px;
height: 18px;
animation: loading-rotate 2s linear infinite;
}
.el-button .custom-loading .circular .path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: var(--el-button-text-color);
stroke-linecap: round;
}
</style>
总结
- 可以通过
defineProps(['onEventName'])
声明事件,组件内部通过props.onEventName()
触发事件,并且可以获取到事件回调函数的返回值,进而组件内部做更多逻辑处理,如给一个按钮组件自动添加loading等 @eventName
本质就是一个语法糖
,最后还是会编译为onEventName
通过属性的形式
传递给组件。如需了解更多,请查看文章:通过编译源码解析Vue不同方式监听事件的区别
结语
感谢您的耐心阅读,如果觉得这篇文章对您有帮助和启发,麻烦给个大大的赞~