我们期望的场景是,
初始化一个请求函数,然后根据初始参数,立即发送请求
返回请求结果,并且返回请求回调函数,方便我们根据新的参数再次调用
要求返回值包含加载中状态,错误信息以及正常的数据类型。
我的实现过程如下:
定义具体的数据返回值签名
interfaceFetch<T>{ loading:boolean,value?: T,// 具体的返回类型是泛型error?: string }
完整的签名如下
exportdeclarefunctionuseFetch<T,Params>(fn:(args: Params)=>Promise<T>,initParams: Params):[Fetch<T>,(params: Params)=>Promise<unknown>]
函数实现如下:
export const useFetch=<T,Params>(fn:(args: Params)=>Promise<T>,initParams: Params):[Fetch<T>,(params: Params)=>Promise<T>]=>{// 定义基础的数据格式constdata=reactive<Fetch<T>>({ loading:true,value:undefined,error:undefined})asFetch<T>// 这里会报错:T类型无法赋值给UnwrapRef<T>类型// 我不懂为啥,UnwrapRef是vue的深层响应式类型的声明// 这里导致我无法默认的类型赋值,不符合直觉,可能是我ts太菜了// 懂的大佬评论区带带我吧// 定义请求回调const callback=(params: Params): Promise<T>=>new Promise((resolve,reject)=>{data.loading=truefn(params).then(result=>{data.value=resultasanyresolve(result)}).catch(error=>{data.error=error reject(error)}).finally(()=>data.loading=false)})// 立即执行watchSyncEffect(()=>{ callback(initParams)})return[data,callback]}
我们验证下
<script setup lang="ts">import{ reactive }from'vue';import{ useFetch }from'./hooks/index';const fn=()=>new Promise((resolve)=>{ setTimeout(()=>resolve({data:[],msg:'',code:200}),1000)})const[data,fetch]=useFetch<unknown,object>(fn,{})</script><template><h4>公众号:萌萌哒草头将军</h4><!-- 加载中时用css禁用按钮 --><button :style="{'pointer-events': data.loading ? 'none' : 'auto'}"@click="fetch({})">{{data.loading ?'laoding...':'fetch'}}</button><h1 v-if="data.loading">loading...</h1><h1 v-else>{{data.value}}</h1></template>
fetch.gif
页面刷新后立即发出请求了!
我们期望的场景是,
初始化一个请求函数
返回请求回调函数,方便我们调用
要求返回值包含加载中状态,错误信息以及正常的数据类型
这个的实现和上面类似,我们不需要初始参数和类型,也不用立即执行,
完整的签名如下
exportdeclarefunctionuseFetch<T>(fn:(args: unknown)=>Promise<T>):[Fetch<T>,(params: unknown)=>Promise<T>]
实现如下:
export const useFetchFn=<T>(fn:(args: unknown)=>Promise<T>):[Fetch<T>,(params: unknown)=>Promise<T>]=>{ constdata=reactive<Fetch<T>>({ loading:false,// 默认为falsevalue:undefined,error:undefined})asFetch<T>const callback=(params: unknown): Promise<T>=>new Promise((resolve,reject)=>{data.loading=truefn(params).then(result=>{data.value=resultasanyresolve(result)}).catch(error=>{data.error=error reject(error)}).finally(()=>data.loading=false)})return[data,callback]}
验证如下:
<script setup lang="ts">import{ reactive }from'vue';import{ useFetchFn }from'./hooks/index';const fn=()=>new Promise((resolve)=>{ setTimeout(()=>resolve({data:[],msg:'',code:200}),1000)})const[data,fetch]=useFetchFn<unknown>(fn)</script><template><h4>公众号:萌萌哒草头将军</h4><!-- 加载中时用css禁用按钮 --><button :style="{'pointer-events': data.loading ? 'none' : 'auto'}"@click="fetch({})">{{data.loading ?'laoding...':'fetch'}}</button><h1 v-if="data.loading">loading...</h1><h1 v-else>{{data.value}}</h1></template>
fetchfn.gif
页面刷新后没有发出请求,点击按钮之后才发出请求!
我们期望的场景是,
初始化一个请求函数,然后根据初始参数,立即发送请求
当参数发生变化时,自动根据最新参数发送请求
要求返回值包含加载中状态,错误信息以及正常的数据类型。
这个的实现和立即请求函数类似,只需要监听参数的变化,
完整的签名如下
exportdeclarefunctionuseFetch<T,Params>(fn:(args: Params)=>Promise<T>,initParams: Params):Fetch<T>
实现如下:
export const useAutoFetch=<T,Params>(fn:(args: Params)=>Promise<T>,initParams: Params):Fetch<T>=>{ constdata=reactive<Fetch<T>>({ loading:true,value:undefined,error:undefined})asFetch<T>const callback=(params: Params): Promise<T>=>new Promise((resolve,reject)=>{data.loading=truefn(params).then(result=>{data.value=resultasanyresolve(result)}).catch(error=>{data.error=error reject(error)}).finally(()=>data.loading=false)})// 如果不需要立即执行,可没有这步const effects=watchSyncEffect(()=>{ callback(initParams)})// 自动监听参数变化const effects=watch([initParams],()=>callback(initParams))// 卸载页面时,关闭监听onUnmounted(()=>effects())returndata}
我们验证下功能
<script setup lang="ts">import{ reactive,watch }from'vue';import{ useAutoFetch }from'./hooks/index';const fn=()=>new Promise((resolve)=>{ setTimeout(()=>resolve({data:[],msg:'',code:200}),1000)})const params=reactive({ age:9})constdata=useAutoFetch<unknown,object>(fn,params)watch(params,()=>console.log(params))</script><template><h4>公众号:萌萌哒草头将军</h4><div>{{ params.age }}</div><!-- 加载中时用css禁用按钮 --><button :style="{'pointer-events': data.loading ? 'none' : 'auto'}"@click="() => params.age++">{{data.loading ?'laoding...':'change params'}}</button><h1 v-if="data.loading">loading...</h1><h1 v-else>{{data.value}}</h1></template>
auto.gif
每次当我们改变参数时自动发送请求。