优化Loading动画响应,告别一闪而过的尴尬


这两天终于空下来了,打算优化下项目,发现在项目中,部分页面请求响应的很快,就会出现loading动画一闪而过的情况,用户体验很差。于是决定优化下loading动画。

直接去除loading动画肯定是不现实的,用户无法感知响应,这个阶段去改造成骨架屏也不合适,那么只能优化loading动画,让它显示的时间更长一些。

我们只要设置一个最小loading时间,当请求响应时间小于这个时间时,就等待剩余时间,这样就可以避免loading动画突然的闪烁。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
async fetchData() {
try {
const minLoadingTime = 800; // 最小loading时间(毫秒)
loading.value = true;
const startTime = Date.now(); // 记录开始时间
// 发起请求
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
const result = await response.json();

// 计算已经过去的时间
const elapsedTime = Date.now() - startTime;

// 如果耗时小于最小loading时间,则等待剩余时间
if (elapsedTime < minLoadingTime) {
await new Promise(resolve => setTimeout(resolve, minLoadingTime - elapsedTime));
}

data.value = result;
} catch (error) {
console.error('请求失败:', error);
alert('数据获取失败,请重试');
} finally {
loading.value = false;
}
}

ok,这样的话,当请求低于800ms时,就会等待剩余时间,也就不会出现loading动画闪烁的情况了。
但是项目中有很多地方会去请求,我们不可能每个地方都加一遍,那么只能放在拦截器上统一处理了。

问题二来了,不是所有请求都需要loading动画的呀,这样的话我们就需要在拦截器上判断下。

首先抽象出loading的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// useRequest.js
import { ref } from 'vue'

export function useRequest(minLoadingTime = 800) {
const loading = ref(false)

const request = async (requestFn) => {
try {
loading.value = true
const startTime = Date.now()

// 执行请求
const result = await requestFn()

// 计算最小展示时间
const elapsedTime = Date.now() - startTime
if (elapsedTime < minLoadingTime) {
await new Promise(resolve => setTimeout(resolve, minLoadingTime - elapsedTime))
}

return result
} catch (error) {
console.error('请求失败:', error)
throw error
} finally {
loading.value = false
}
}

return {
loading,
request
}
}

接下来对拦截器进行改造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
useAxios.js
import { ref } from 'vue'
import axios from 'axios'
import { useRequest } from './useRequest'

export function useAxios(config = {}) {
const data = ref(null)
const error = ref(null)

const {
withLoading = false,
minLoadingTime = 800,
...axiosConfig
} = config

const instance = axios.create(axiosConfig)
const { loading, request: withMinLoading } = useRequest(minLoadingTime)

const execute = async (requestConfig) => {
try {
error.value = null

if (withLoading) {
const res = await withMinLoading(() => instance(requestConfig))
data.value = res.data
return res
} else {
const res = await instance(requestConfig)
data.value = res.data
return res
}
} catch (err) {
error.value = err
throw err
}
}

return {
data,
error,
loading,
execute,
}
}
最后调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 需要 loading 的请求
const {
data:,
loading,
execute: fetchPosts,
} = useAxios({
withLoading: true,
minLoadingTime: 800,
baseURL: 'https://jsonplaceholder.typicode.com' // 添加基础URL
})

// 不需要 loading 的请求
const {
data,
execute: fetchPostsNoLoading,
} = useAxios({
baseURL: 'https://jsonplaceholder.typicode.com' // 添加基础URL
})

// 带 loading 的请求处理函数
// 这里是点击按钮1执行带loading的请求
const handleFetchWithLoading = () => {
fetchPosts({
method: 'get',
url: '/posts',
params: {
_limit: 5 // 限制返回5条数据
}
})
}

// 不带 loading 的请求处理函数
// 这里是点击按钮2执行不带loading的请求
const handleFetchNoLoading = () => {
fetchPostsNoLoading({
method: 'get',
url: '/posts',
params: {
_limit: 5 // 限制返回5条数据
}
})
}

ok大功告成,这样就可以根据不同场景控制loading动画了。

我的微信公众号: 梨的前端小屋


  目录