首页>>前端>>Vue->一看就会的超实用小组件之LoadingButton

一看就会的超实用小组件之LoadingButton

时间:2023-11-30 本站 点击:1

组件背景

在平时的工作中,经常会遇到一个场景:

点击按钮时请求一些接口数据,而为了避免用户重复的点击我们通常会为这些按钮添加loading。这个添加loading的功能本身时非常简单的,只要我们定义一个变量使用在Button组件中即可,但在做后台管理类项目时,这样的按钮可能会有非常非常多,可能一个组件中,很多变量都是xxx_loading,耗时耗力又不够优雅。 接下来,我们对Button组件做一个简单的封装来解决这个耗时耗力又不够优雅的loading问题

灵感来源

我们在使用Antd的Modal对话框时,当我们的onOk异步函数时,此时Modal的确定按钮会自动添加loading效果,在函数执行完成后关闭弹窗,就像这样: 此时,代码如下:

asyncFunc() {return new Promise(resolve => {setTimeout(() => {resolve()}, 2000)})},handleTestModal() {const that = thisthis.$confirm({title: '测试异步函数',content: '异步函数延迟两秒结束',async onOk() {await that.asyncFunc()}})},

看到这种效果后,就想到,如果可以封装一个Button组件,将需要执行的函数传入,组件中自动根据函数执行情况添加loading效果岂不是非常的方便。

实现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事件,代码如下:

<template>  <Button :type="type" :loading="loading" @click="handleClick">    {{ text }}  </Button></template><script>import { Button } from 'ant-design-vue'export default {    components: {        Button    },    methods: {        handleClick() {}    }}</script>

判断异步函数asyncFunc

这一部分为整个组件最重要的一个部分,即我们如何去判断传入的函数是异步函数,当我们传入的asyncFunc函数是异步函数时,组件才需要添加loading的动画,那么我们应该如何去判断一个函数是否为异步函数呢?

参考antd是如何实现的?

上面我们刚介绍了antdModal对话框中有类似的逻辑,那么不妨去阅读一下这部分相关的源码,看下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组件

到这里我们的最核心的组件逻辑就开发完成了,后面我们写一个demo来测试一下这个LoadingButton组件是否符合预期:demo代码如下:

<template><div><LoadingButton :delay="500" :asyncFunc="asyncFunc" /></div></template>
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变量。

写在最后

这个组件其实核心的代码非常少,也很容易读懂。由于最近在做一些业务这类场景比较多,感觉这个小组件还是挺实用的所以分享给大家,这里也是只对最重要的部分做了一个介绍,相信大家学会了之后也可以通过这个方式封装出符合自己实际场景需求的组件。最后,附上这个组件的完整代码:

<template>  <Button :type="type" :loading="loading" @click="handleClick">    {{ text }}  </Button></template><script>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        })      }    }  }}</script>

感谢阅读 ?

原文:https://juejin.cn/post/7099234795720278046


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Vue/3797.html