likes
comments
collection
share

教你如何在vue项目中使用‘useState’并添加额外的魔法

作者站长头像
站长
· 阅读数 10

教你如何在vue项目中使用‘useState’并添加额外的魔法

用法一览

import useLoading from '@/useHooks/useLoading.js';
const [loading, setLoading] = useLoading();
setLoading(true) //setLoading(false)

起源

这种奇奇怪怪的代码背后总会有一个奇奇怪怪的需求。某天产品经理找到我说:小王啊,我发现我们公司平台的页面按钮的加载状态就是闪一下,动画都没展示完就结束了,用户体验不是很好啊。

此时我内心的OS是:接口请求太快了也能怪我吗。但是毕竟是吩咐下来的任务,该完成还是要完成的而且这个需求看起来好像也并不是很复杂啊,那就整。

需求分析

产品说按钮的加载状态太快,那直接原因就是接口返回的速度太快了,当然我们不能减缓网络请求的速度,那么就只有一种办法,延长我们 loading 为 true 的时间。那么就出现了最粗暴的办法。

import { ref } from 'vue'
const loading = ref(false)
const fn = async ()=>{
    loading.value = true;
    const {data,code} = await xxx()
    setTimeout(()=>{loading.value = false},1000)
}
fn()

当然这个方法有个致命的问题,就是如果xxx 请求返回的时间也很慢,那这个延迟修改加载状态无疑是雪上加霜了,直接导致体验负优化,那么我们要做的就是给loading状态加上一个兜底。即:当loading时间小于某个值的时候 loading状态并不会变化,只有超过某个时间值的时候我们才允许loading状态的改变。有了目标,那就开始想怎么实现。

首先,我们要有一个值去记录是否达到我们的兜底时间,而且从实现上来说我们只用考虑loading状态从false变为true的中间的时间。

其次还要有一个值记录真实的状态,即异步操作结束的状态。

最后就是我们的综合loading状态依赖于这两个状态,有一个还在加载那么就还在加载。

功能实现

直接上代码:

// useLoading.js
import { ref, computed } from 'vue';

/**
 * @param {number} time 兜底的时间 单位ms
 */
export default (time = 500) => {
    // 变量写入函数内避免数据污染
    const isCounting = ref(false) // 是否在兜底时间
    const isLoading = ref(false) // 真实的加载状态

    const countDown = (time) => {
        if (typeof time !== 'number' || isNaN(time)) throw new Error(`expect a [object Number] but got ${isNaN(time) ? 'NaN' : Object.prototype.toString.call(time)}`) // 参数校验

        if (isCounting) return // 以防万一

        isCounting.value = true // 开始计时

        let timer = setTimeout(() => {
            isCounting.value = false // 结束计时
            clearTimeout(timer)
        }, time)
    }

    const setLoaing = (val) => {
        isLoading.value = val
        val && countDown(time)
    }

    const loading = computed(
        () => isCounting.value || isLoading.value, // 真正loaing中或者没结束倒数时都是loading状态
    )

    return [loading, setLoaing]
}

还记得我们开头的用法吗,不记得的同学可以再回去看看,到此这个奇怪的需求就被完美的解决了。

写在最后

其实写这篇文章的目的也并不是说我实现了一个怎么样的功能,更重要的是解决问题的思路,当然有思路也要有相应的直觉或者叫灵感才能写的出优秀的代码(此处并不是自夸,笔者写代码的时候也是推翻重来了好多次。)

当然解决思路也并不只有这一种,此处我再抛砖引玉一下 :其实可以从v-loading这个自定义指令入手或者对el-button进行二次封装,笔者用的vue2.7版本,element-ui 中业务组件还是使用的optionAPI 故可以使用extends来解决问题。