本篇文章手把手帶大家開發一個超實用的vue加載Button組件–LoadingButton,希望對大家有所幫助。

組件背景
在平時的工作中,經常會遇到一個場景:

點擊按鈕時請求一些接口數據,而為了避免用戶重復的點擊我們通常會為這些按鈕添加loading。這個添加loading的功能本身時非常簡單的,只要我們定義一個變量使用在Button組件中即可,但在做后臺管理類項目時,這樣的按鈕可能會有非常非常多,可能一個組件中,很多變量都是xxx_loading,耗時耗力又不夠優雅。
接下來,我們對Button組件做一個簡單的封裝來解決這個耗時耗力又不夠優雅的loading問題。(學習視頻分享:vue視頻教程)
靈感來源
我們在使用Antd的
Modal對話框時,當我們的onOk為異步函數時,此時Modal的確定按鈕會自動添加loading效果,在函數執行完成后關閉彈窗,就像這樣:

此時,代碼如下:
asyncFunc() { return new Promise(resolve => { setTimeout(() => { resolve() }, 2000) }) }, handleTestModal() { const that = this this.$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是如何實現的?
上面我們剛介紹了antd的Modal對話框中有類似的邏輯,那么不妨去閱讀一下這部分相關的源碼,看下antd的實現方式:
// components/modal/ActionButton.jsx onClick() { 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> <script> import LoadingButton from './LoadingButton.vue' export default { data() { return { loading: false } }, components: { LoadingButton }, methods: { asyncFunc() { return new Promise(resolve => { setTimeout(() => { resolve() }, 2000) }) } } } </script>
我們寫了一個異步函數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
作者:liangyue
(學習視頻分享:web前端開發、編程基礎視頻)
站長資訊網