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
  }
}
相关推荐
cch89181 天前
PHP vs Vue.js:后端与前端的终极对比
前端·vue.js·php
我命由我123451 天前
Vue3 开发中,字符串中的 <br\> 标签被直接当作文本显示出来了,而不是被解析为 HTML 换行标签
开发语言·前端·javascript·vue.js·html·ecmascript·html5
北风toto1 天前
Vue多文件学习项目综合案例——面经基础版,黑马vue教程
javascript·vue.js·学习
徐小夕1 天前
我用 AI 撸了个开源"万能预览器":浏览器直接打开 Office、CAD 和 3D 模型
前端·vue.js·github
前端Hardy2 天前
字节/腾讯内部流出!Claude Code 2026王炸玩法!效率暴涨10倍
前端·javascript·vue.js
逆光如雪2 天前
移动端卡片边框怎么做高级?我用 CSS 实现了设计师的刁钻要求
前端·css·vue.js
莹宝思密达2 天前
地图显示西安经济开发区边界线-2023.12
前端·vue.js·数据可视化
leafyyuki2 天前
告别 Vuex 的繁琐!Pinia 如何以更优雅的方式重塑 Vue 状态管理
前端·javascript·vue.js
Hooray2 天前
AI 时代的管理后台框架,应该是什么样子?
前端·vue.js·ai编程
代码煮茶2 天前
Vue3 虚拟列表实战 | 解决长列表性能问题(十万条数据流畅渲染,附原理)
前端·javascript·vue.js