维护loading加载状态的几个方法小结
作者:vilan_微澜
前言
在项目开发中,当页面请求接口时,组件局部或者页面全局显示loading
加载遮罩可谓是司空见惯了,下面来讨论一下如何优雅的使用loading
状态。下文引用了比较常用的两个组件Button
按钮和Table
表格作为例子,谈谈如何优雅的维护loading
加载状态。
Butoon组件原始加载用法
<template> <div> <div>数据:{{ list }}</div> <el-button type="primary" @click="asyncFetch" :loading="loading">异步获取</el-button> <el-button type="primary" @click="syncFetch" :loading="loading">同步获取</el-button> </div> </template> <script setup> import { ref } from 'vue' import { FetchApi } from '@/api' const loading = ref(false) const list = ref([]) // 异步调用 const asyncFetch = () => { loading.value = true FetchApi().then(res => { console.log(res) list.value = res }).finally(() => { loading.value = false }) } // 同步调用 const syncFetch = async () => { loading.value = true try { const res = await FetchApi() console.log(res) list.value = res } catch (error) { console.log(error) } loading.value = false } </script>
由以上代码可看出,在每次点击按钮提交时,都需要在调用接口前开启加载状态,在接口调用结束时,无论成功与否,都结束加载状态。为了简化这一操作步骤,减少代码冗余,我们可以封装一个button
组件,把loading
状态及相关逻辑提取这个组件当中。
封装Button组件
<template> <!-- 新版本vue中,透传可省略v-bind="$attrs" --> <el-button v-bind="$attrs" :loading="loading" :onClick="handleClick"> <slot></slot> </el-button> </template> <script setup> import { ref } from 'vue' const { onClick } = defineProps(['onClick']) const loading = ref(false) // 重写点击方法 const handleClick = async () => { loading.value = true try { // 传递的onClick方法必须为同步的 onClick && await onClick() } catch (error) { console.error(error) } loading.value = false } </script>
以上对button
组件进行二次封装,把一些重复的业务逻辑放到该组件中,为了能够知道异步方法什么时候执行结束,将点击方法
通过prop
参数的形式传递到子组件中,交给子组件在合适的时机执行,学过react
的话应该对此操作比较熟悉。
组件加载用法
Page.vue
<template> <div> <div>数据:{{ list }}</div> <ViButton type="primary" :onClick="syncFetch">同步获取</ViButton> </div> </template> <script setup> import { ref } from 'vue' import { FetchApi } from '@/api' import ViButton from '@/components/ViButton/ViButton.vue' const list = ref([]) // 同步调用 const syncFetch = async () => { const res = await FetchApi() list.value = res } </script>
需要注意的是,我们需要把FetchApi()
异步方法转为同步,这是为了在Button
组件中能够方便地知道FetchApi方法
的执行结束时机,否则在Button
组件中将不能正确的隐藏loading
状态。
封装表格组件
<template> <!-- 新版本vue中,透传可省略v-bind="$attrs" --> <el-table v-bind="$attrs" v-loading="loading"> <slot></slot> </el-table> </template> <script setup> import { ref, onMounted } from 'vue' const props = defineProps({ // 获取数据方法 fetchData: { type: Function, default: () => {} }, // 默认是onMounted立即执行fetchData immediateFetch: { type: Boolean, default: true } }) const loading = ref(false) const getList = async () => { loading.value = true try { await props.fetchData() } catch (error) { console.log(error) } loading.value = false } onMounted(() => { props.immediateFetch && getList() }) // 暴露经过loading处理的fetch方法,比如搜索时候需要在父组件手动调用 defineExpose({ fetchData: getList }) </script>
组件加载用法
page.vue
<template> <div> <ViTable ref="ViTableRef" :data="list" :fetchData="syncFetch" :immediateFetch="false"> <el-table-column prop="name" label="姓名"></el-table-column> </ViTable> <el-button @click="onSearch">查询</el-button> </div> </template> <script setup> import { ref } from 'vue' import { FetchApi } from '@/api' import ViTable from '@/components/ViTable/ViTable.vue' const list = ref([]) const ViTableRef = ref(null) // 同步调用 const syncFetch = async () => { console.log('syncFetch') const res = await FetchApi() list.value = res console.log(list.value) } // 搜索 const onSearch = () => { ViTableRef.value.fetchData() } </script>
以上需要注意的有两点:
- 数据是否需要在表格挂载完成立即获取;
- 需要对外暴露经过
loading
加工的fetch
方法,目的是满足需要手动调用获取数据的需求,比如点击搜索;
以上方式当需要手动调用时似乎还是有些麻烦,下面我们改成useHook
方式来试试~
封装useLoading钩子函数
useLoading.js
import { ref } from 'vue' // 多个组件共享统一状态 const loading = ref(false) export const useLoading = (prop = { fetchData: () => {} }) => { // 对外暴露loading给table组件或者button组件使用 // 内部只负责处理业务逻辑,不负责调用,这样更加灵活 return { loading, fetchData: async () => { loading.value = true try { await prop.fetchData() } catch (error) { console.log(error) } loading.value = false } } }
ViTablePlus.vue
<template> <!-- 新版本vue中,透传可省略v-bind="$attrs" --> <el-table v-bind="$attrs" v-loading="loading"> <slot></slot> </el-table> </template> <script setup> import { useLoading } from '@/hooks/useLoading' const { loading } = useLoading() </script>
用法
Page.vue
<template> <div> <ViTablePlus :data="list" :fetchData="syncFetch" :immediateFetch="false"> <el-table-column prop="name" label="姓名"></el-table-column> </ViTablePlus> <el-button @click="onSearch">查询</el-button> </div> </template> <script setup> import { ref, onMounted } from 'vue' import { FetchApi } from '@/api' import ViTablePlus from '@/components/ViTablePlus/ViTablePlus.vue' import { useLoading } from '@/hooks/useLoading' // 手动执行经过loading处理的fetchData方法 const { fetchData } = useLoading({ fetchData: syncFetch }) const list = ref([]) // 需要使用function定义方法,进行变量提升,因为useLoading在syncFetch方法定义前执行 async function syncFetch() { const res = await FetchApi() list.value = res } // 搜索 const onSearch = () => { // 经过loading处理的fetchData方法 fetchData() } onMounted(() => { // 经过loading处理的fetchData方法 fetchData() }) </script>
总结
本文讲了在二次封装的Button
和Table
组件内部维护loading
状态,为了能够获取到异步方法的调用完成时机,我们把异步方法
转为同步方法
后,通过prop
的方式传递到子组件,委托子组件在恰当的时机执行。但把loading
状态封装到组件内部还是不够灵活,最后使用useLoading
钩子函数维护一个全局状态,提供灵活的跨组件状态共享。
以上就是维护loading加载状态的几个方法小结的详细内容,更多关于维护loading加载状态的资料请关注脚本之家其它相关文章!