vueuse/core-computedAsync函数

vue提供的computed是不支持异步的,而vueuse的computedAsync支持

使用场景:联动下拉框、搜索等地方,输入关键词触发ajax请求接口,用户输入的过程中取消上一次ajax请求

1、使用demo

vue 复制代码
<template>
  <div>{{ userInfo }}<button @click="handleChange">改变</button></div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { computedAsync } from '@vueuse/core'

function mockAjax (params) {
  return new Promise(resolve => {
    setTimeout(() => {resolve({code:0,data: {name:params}})}, 1000)
  })
}

const name = ref('jack')

const userInfo = computedAsync(
  async () => {
    return await mockAjax(name.value)
  },
  null, // initial state
)

function handleChange () {
  name.value = 'lucy'
}
</script>

2、参数

2.1 第3个参数可以接收一个boolean类型,判断异步是否处于进行中

这样就可以方便的实现请求中出现loading的效果

ts 复制代码
import { ref } from 'vue'
import { computedAsync } from '@vueuse/core'

const evaluating = ref(false) // 可以用来做loading状态

const userInfo = computedAsync(
  async () => { /* your logic */ },
  null,
  evaluating,
)

也可以接受一个options,配置各种信息,如下:

ts 复制代码
{
    lazy: boolean; // 是否懒执行
    evaluating: boolean; // 是否处理中
    shallow: boolean; // 是否使用shallow
    onError: (e: unknown) => void // 当捕获到错误时的回调函数
}

2.2 取消回调

在第1个参数可以接收一个onCancel回调,当上一个异步还在处理中的时候,如果执行了computedAsync的,就会触发onCancel回调

vue 复制代码
<template>
  <div>{{evaluating}} / {{ userInfo }}<button @click="handleChange">改变</button></div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { computedAsync } from '@vueuse/core'

function mockAjax (params) {
  return new Promise(resolve => {
    setTimeout(() => {resolve({code:0,data: {name:params}})}, 1000)
  })
}

const name = ref('jack')
const evaluating = ref(false)
const userInfo = computedAsync(
  async (onCancel) => {
    onCancel(() => {
      console.log('canceled'); // 在这里,我们可以通过取消axios来取消上一次请求的,demo可见:https://vueuse.org/core/computedAsync/#oncancel
    });
    return await mockAjax(name.value)
  },
  null, // initial state
  evaluating
)

function handleChange () {
  name.value = name.value +'j';
}
</script>

2.3 lazy懒执行

默认情况下,computedAsync 会在创建时立即开始解析,指定 lazy: true 可使其在第一次访问时开始解析。

vue 复制代码
<template>
</template>
<script lang="ts" setup>
const userInfo = computedAsync(
  async (onCancel) => {
    onCancel(() => {
      console.log('canceled')
    });
    console.log('发起ajax');
    return await mockAjax(name.value)
  },
  null,
  {lazy: true, evaluating}
)
</script>

想上面代码中只有定义了userInfo但没有使用,则 "发起ajax" 是不会执行的

3、源码

ts 复制代码
export function computedAsync(evaluationCallback, initialState, optionsOrRef) {
  let options
  
  // 磨平第3个参数传参不同的差异,并解构options配置
  // 只传了一个值,就是处理 computedAsync(()=>{},null,ref(false))第3个参数是一个ref的情况
  if (isRef(optionsOrRef)) {
    options = {
      evaluating: optionsOrRef,
    }
  }
  else {
    options = optionsOrRef || {}
  }
  const {
    lazy = false,
    evaluating = undefined,
    shallow = true,
    onError = noop,
  } = options

  const started = ref(!lazy)
  const current = (shallow ? shallowRef(initialState) : ref(initialState)) as Ref<T>
  let counter = 0

  watchEffect(async (onInvalidate) => {
    // 当传了 {lazy:true} started=false,初次执行到这里就停了
    // 直到有地方调用结果.value,就会触发后面的computed,里面会设置started=true
    if (!started.value)
      return

    counter++
    const counterAtBeginning = counter
    let hasFinished = false

    // Defer initial setting of `evaluating` ref
    // to avoid having it as a dependency
    // 推迟初始设置 `evaluating` ref
    // 以避免将其作为依赖项
    if (evaluating) {
      Promise.resolve().then(() => {
        evaluating.value = true
      })
    }

    try {
      const result = await evaluationCallback((cancelCallback) => {
        onInvalidate(() => {
          if (evaluating)
            evaluating.value = false

          // 如果上一个还没执行完成,就执行cancelCallback,可以在里面取消上一次异步请求
          if (!hasFinished)
            cancelCallback()
        })
      })
      // 当上一次异步还没执行完,依赖项发生改变就会再调一次evaluationCallback()
      // counterAtBeginning是当前第N次执行,counter是最后第N次,当2个值相同的时候,说明是最后一次执行了
      if (counterAtBeginning === counter)
        current.value = result
    }
    catch (e) {
      onError(e)
    }
    finally {
      if (evaluating && counterAtBeginning === counter)
        evaluating.value = false

      hasFinished = true
    }
  })

  if (lazy) {
    return computed(() => {
      started.value = true
      return current.value
    })
  }
  else {
    return current
  }
}
相关推荐
能来帮帮蒟蒻吗27 分钟前
VUE3 -综合实践(Mock+Axios+ElementPlus)
前端·javascript·vue.js·笔记·学习·ajax·typescript
Java&Develop2 小时前
Vue ElementUI原生upload修改字体大小和区域宽度
vue.js
郭尘帅6663 小时前
vue3基础学习(上) [简单标签] (vscode)
前端·vue.js·学习
st紫月4 小时前
用vue和go实现登录加密
前端·vue.js·golang
岁岁岁平安4 小时前
Vue3学习(组合式API——计算属性computed详解)
前端·javascript·vue.js·学习·computed·计算属性
工业互联网专业7 小时前
基于springboot+vue的医院门诊管理系统
java·vue.js·spring boot·毕业设计·源码·课程设计·医院门诊管理系统
九月TTS7 小时前
TTS-Web-Vue系列:Vue3实现内嵌iframe文档显示功能
前端·javascript·vue.js
我爱加班、、7 小时前
Chrome安装最新vue-devtool插件
javascript·vue.js·chrome·vue-devtool
澄江静如练_8 小时前
小程序 存存上下滑动的页面
前端·javascript·vue.js
源码方舟8 小时前
基于SpringBoot+Vue的房屋租赁管理系统源码包(完整版)开发实战
vue.js·spring boot·后端