点击按钮时请求一些接口数据,而为了避免用户重复的点击我们通常会为这些按钮添加loading。这个添加loading
的功能本身时非常简单的,只要我们定义一个变量使用在Button
组件中即可,但在做后台管理类项目时,这样的按钮可能会有非常非常多,可能一个组件中,很多变量都是xxx_loading
,耗时耗力又不够优雅。
接下来,我们对Button
组件做一个简单的封装来解决这个耗时耗力又不够优雅的loading
问题。(学习视频分享:vue视频教学)
灵感来源
此时,代码如下:
asyncFunc() { return new Promise(resolve => { setTimeout(() => { resolve() }, 2000) })},handleTestModal() { const that = this this.$confirm({ title: '测试异步函数', content: '异步函数延迟两秒结束', async onOk() { await that.asyncFunc() } })},
实现LoadingButton
定义组件参数
这边就定义几个大家会常用到的参数:text(按钮文字)
、type(按钮类型)
、asyncFunc(按钮点击时执行的异步函数)
、delay(loading延迟)
,另外,还需要一个组件内部的loading
变量来控制我们Button
组件的状态,代码如下:
export default { data() { return { loading: false } }, props: { text: { type: String, default: '确定' }, type: { type: String, default: 'primary' }, delay: { type: Number, default: 0 }, asyncFunc: { type: Function, default: () => {} } },}
使用antd
中的Button
组件进行二次封装
在我们的自定义LoadingButton
组件中,将上面定义的参数使用起来,并绑定一个click
事件,代码如下:
import { Button } from 'ant-design-vue'export default { components: { Button }, methods: { handleClick() {} }}
判断异步函数asyncFunc
参考antd
是如何实现的?
上面我们刚介绍了antd
的Modal
对话框中有类似的逻辑,那么不妨去阅读一下这部分相关的源码,看下antd
的实现方式:
// components/modal/ActionButton.jsxonClick() { const { actionFn, closeModal } = this; if (actionFn) { let ret; if (actionFn.length) { ret = actionFn(closeModal); } else { ret = actionFn(); if (!ret) { closeModal(); } } if (ret && ret.then) { this.setState({ loading: true }); ret.then( (...args) => { // It's unnecessary to set loading=false, for the Modal will be unmounted after close. // this.setState({ loading: false }); closeModal(...args); }, e => { // Emit error when catch promise reject // eslint-disable-next-line no-console console.error(e); // See: https://github.com/ant-design/ant-design/issues/6183 this.setState({ loading: false }); }, ); } } else { closeModal(); }},
阅读antd
源码的实现,我们知道,判断一个函数是否是异步函数,可以通过判断函数是否有.then
(ret && ret.then
)方法,那么我们也可以类似的做一个判断,代码如下:
async handleClick() { const asyncFunc = this.asyncFunc if (!this.isFunc) { return } const ret = asyncFunc() // 如果是异步函数,则显示loading if (ret && ret.then) { this.loading = { delay: this.delay } ret.finally(() => { this.loading = false }) }}
测试LoadingButton
组件
import LoadingButton from './LoadingButton.vue'export default { data() { return { loading: false } }, components: { LoadingButton }, methods: { asyncFunc() { return new Promise(resolve => { setTimeout(() => { resolve() }, 2000) }) } }}
我们写了一个异步函数asyncFunc
用来模拟实际业务中的异步请求,现在可以看下效果:
符合之前的预期效果,这样我们再有类似需要loading
的场景时,就可以直接使用LoadingButton
组件,将点击需要执行的异步函数传入即可,不需要再去定义loading
变量。
写在最后
import { Button } from 'ant-design-vue'export default { data() { return { loading: false } }, props: { text: { type: String, default: '确定' }, type: { type: String, default: 'primary' }, delay: { type: Number, default: 0 }, asyncFunc: { type: Function, default: () => {} } }, components: { Button }, computed: { isFunc() { return typeof this.asyncFunc === 'function' } }, methods: { async handleClick() { const asyncFunc = this.asyncFunc if (!this.isFunc) { return } const ret = asyncFunc() // 如果是异步函数,则显示loading if (ret && ret.then) { this.loading = { delay: this.delay } ret.finally(() => { this.loading = false }) } } }}
(学习视频分享:web前端开发、编程基础视频)