這篇文章手把手帶大家開發一個超實用的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
效果豈不是非常的方便。
#定義元件參數
##這邊就定義幾個大家會常用的參數: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<span style="font-size: 18px;"></span>
中的Button<span style="font-size: 18px;"></span>
元件進行二次封裝
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<span style="font-size: 18px;"></span>
asyncFunc參考函數是非同步函數時,元件需要添加loading的動畫,那麼我們應該如何去判斷一個函數是否為非同步函數呢?
antd是如何實現的? 上面我們剛剛介紹了
的Modal
對話方塊中有類似的邏輯,那麼不妨去閱讀這部分相關的源碼,看看antd
的實作方式:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">// 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&#39;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();
}
},</pre><div class="contentsignin">登入後複製</div></div>
閱讀
原始碼的實現,我們知道,判斷一個函數是否是非同步函數,可以透過判斷函數是否有 .then
(ret && ret.then
)方法,那麼我們也可以類似的做一個判斷,程式碼如下:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">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
})
}
}</pre><div class="contentsignin">登入後複製</div></div>
LoadingButton元件
LoadingButtonasyncFunc元件是否符合預期: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>登入後複製
用來模擬實際業務中的非同步請求,現在可以看效果:
#符合先前的預期效果,這樣我們再有類似需要
loading的場景時,就可以直接使用 这个组件其实核心的代码非常少,也很容易读懂。由于最近在做一些业务这类场景比较多,感觉这个小组件还是挺实用的所以分享给大家,这里也是只对最重要的部分做了一个介绍,相信大家学会了之后也可以通过这个方式封装出符合自己实际场景需求的组件。最后,附上这个组件的完整代码: 原文地址:https://juejin.cn/post/7099234795720278046 作者:liangyue 以上是vue元件實戰:開發一個載入Button元件--LoadingButton的詳細內容。更多資訊請關注PHP中文網其他相關文章!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>