javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > 维护loading加载状态

维护loading加载状态的几个方法小结

作者:vilan_微澜

在项目开发中,当页面请求接口时,组件局部或者页面全局显示loading加载遮罩可谓是司空见惯了,下面来讨论一下如何优雅的使用loading状态,文中通过代码示例介绍的非常详细,需要的朋友可以参考下

前言

在项目开发中,当页面请求接口时,组件局部或者页面全局显示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>

以上需要注意的有两点:

以上方式当需要手动调用时似乎还是有些麻烦,下面我们改成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>

总结

本文讲了在二次封装的ButtonTable组件内部维护loading状态,为了能够获取到异步方法的调用完成时机,我们把异步方法转为同步方法后,通过prop的方式传递到子组件,委托子组件在恰当的时机执行。但把loading状态封装到组件内部还是不够灵活,最后使用useLoading钩子函数维护一个全局状态,提供灵活的跨组件状态共享。

以上就是维护loading加载状态的几个方法小结的详细内容,更多关于维护loading加载状态的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文