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
  }
}
相关推荐
golitter.43 分钟前
Vue组件库Element-ui
前端·vue.js·ui
道爷我悟了1 小时前
Vue入门-指令学习-v-on
javascript·vue.js·学习
.生产的驴2 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
老齐谈电商2 小时前
Electron桌面应用打包现有的vue项目
javascript·vue.js·electron
LIURUOYU4213082 小时前
vue.js组建开发
vue.js
九圣残炎3 小时前
【Vue】vue-admin-template项目搭建
前端·vue.js·arcgis
《源码好优多》3 小时前
基于SpringBoot+Vue+Uniapp的植物园管理小程序系统(2024最新,源码+文档+远程部署+讲解视频等)
vue.js·spring boot·uni-app
计算机学姐3 小时前
基于微信小程序的调查问卷管理系统
java·vue.js·spring boot·mysql·微信小程序·小程序·mybatis
黄尚圈圈7 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
一路向前的月光12 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js