教你如何在vue项目中使用'useState'并添加额外的魔法
用法一览
javascript
import useLoading from '@/useHooks/useLoading.js';
const [loading, setLoading] = useLoading();
setLoading(true) //setLoading(false)
起源
这种奇奇怪怪的代码背后总会有一个奇奇怪怪的需求。某天产品经理找到我说:小王啊,我发现我们公司平台的页面按钮的加载状态就是闪一下,动画都没展示完就结束了,用户体验不是很好啊。
此时我内心的OS是:接口请求太快了也能怪我吗。但是毕竟是吩咐下来的任务,该完成还是要完成的而且这个需求看起来好像也并不是很复杂啊,那就整。
需求分析
产品说按钮的加载状态太快,那直接原因就是接口返回的速度太快了,当然我们不能减缓网络请求的速度,那么就只有一种办法,延长我们 loading 为 true 的时间。那么就出现了最粗暴的办法。
javascript
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状态依赖于这两个状态,有一个还在加载那么就还在加载。
功能实现
直接上代码:
javascript
// 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来解决问题。