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
  }
}
相关推荐
jyp2012110716 分钟前
Vue3 Diff 算法
前端·vue.js
xn713326 分钟前
AI SDK 7 迁移实战:TypeScript 通过后,生产环境还会坏在哪里?
vue.js·人工智能·后端
smallcelebration1 小时前
139 @vueuse/core依赖介绍使用
前端·javascript·vue.js
蒜苔肉丝1 小时前
Vue3 动态表单:模块复用 + 动态增删行 + 数据回显
前端·javascript·vue.js
daols8811 小时前
vxe-table 渲染器教程一:实现金额输入控件
javascript·vue.js·vxe-table
不好听61311 小时前
React vs Vue:两大前端框架技术选型深度对比
前端·vue.js·react.js
腻害兔13 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
腻害兔14 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程
Larcher19 小时前
LangChain RAG 排错实录:.env 为什么没有生效
vue.js·后端
一个写前端的20 小时前
基于 Vue 3 的大文件分片上传组件,支持断点续传、并发上传、失败重试等功能
前端·javascript·vue.js