This article will guide you step by step in developing a super practical vueLoadingButton component--LoadingButton. I hope it will be helpful to everyone.
In daily work, we often encounter a scene:
Request some interface data when clicking a button, and in order to avoid repeated clicks by users, we usually add loading to these buttons. The function of adding loading
itself is very simple. As long as we define a variable and use it in the Button
component, but when doing background management projects, such a button may There are many, many variables in one component may be xxx_loading
, which is time-consuming, labor-intensive and not elegant enough.
Next, we make a simple encapsulation of the Button
component to solve this time-consuming, labor-intensive and inelegant loading
problem. (Learning video sharing: vue video tutorial)
We are using Antd
Modal
dialog box, when ouronOk
isasynchronous function
, the OK button ofModal
will automatically addloading
Effect, close the pop-up window after the function execution is completed, like this:
At this time, the code is as follows:
asyncFunc() { return new Promise(resolve => { setTimeout(() => { resolve() }, 2000) }) }, handleTestModal() { const that = this this.$confirm({ title: '测试异步函数', content: '异步函数延迟两秒结束', async onOk() { await that.asyncFunc() } }) },
After seeing this effect, I thought that if I could encapsulate a
Button
component and pass in the function that needs to be executed, the component would automatically addloading
according to the function execution. What effect would it have? Not very convenient.
Define component parameters
Just define a few here Commonly used parameters: text (button text)
, type (button type)
, asyncFunc (asynchronous function executed when the button is clicked)
, delay(loading delay)
, in addition, a loading
variable inside the component is needed to control the state of our Button
component, the code is as follows:
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: () => {} } }, }
Use the <span style="font-size: 18px;">Button</span>
component in <span style="font-size: 18px;">antd</span>
to perform the second step Sub-encapsulation
In our custom LoadingButton
component, use the parameters defined above and bind a click
event, The code is as follows:
<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>
Judge asynchronous function<span style="font-size: 18px;">asyncFunc</span>
This part is The most important part of the entire component is how we determine whether the incoming function is an asynchronous function. When the
asyncFunc
function we pass in is an asynchronous function, the component needs to add a loading animation, then we should How to determine whether a function is an asynchronous function?
Referenceantd
How is it implemented?
We just introduced the Modal
dialog box of antd
above. There is similar logic in it, so you might as well read this part of the related source code and take a look. Implementation method of 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(); } },
Read the implementation of antd
source code. We know that to determine whether a function is an asynchronous function, we can determine whether the function has .then
(ret && ret.then
) method, then we can also make a similar judgment. The code is as follows:
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 }) } }
TestLoadingButton
Component
At this point our core component logic has been developed. Later we will write a demo to test whether this
LoadingButton
component meets expectations: demo The code is as follows:
<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>
We wrote an asynchronous function asyncFunc
to simulate asynchronous requests in actual business. Now you can see the effect:
is in line with the previous expected effect, so that when we have similar scenarios that require loading
, we can directly use the LoadingButton
component to click the asynchronous function that needs to be executed. Just pass it in, there is no need to define the loading
variable.
这个组件其实核心的代码非常少,也很容易读懂。由于最近在做一些业务这类场景比较多,感觉这个小组件还是挺实用的所以分享给大家,这里也是只对最重要的部分做了一个介绍,相信大家学会了之后也可以通过这个方式封装出符合自己实际场景需求的组件。最后,附上这个组件的完整代码:
<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
The above is the detailed content of Vue component practice: Develop a loading Button component--LoadingButton. For more information, please follow other related articles on the PHP Chinese website!