Vue中watch清除过期副作用的案例详解

 更新时间:2023年01月03日 09:19:36   作者:无敌小书包  
在这里就不过多说watch的用法了,这篇文章主要通过案例带大家了解一下如何清除过期的副作用。文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下

在这里就不过多说watch的用法了,主要了解一下如何清除过期的副作用

通过一个案例来说吧:

一个可搜索的下拉选择器,用户第一次进行搜索的时候网速比较慢,请求虽然被服务端正确响应了,但是数据一直没有传输到客户端,用户看下拉数据没变化 就第二次搜索。第二次搜索之后网速恢复正常了,第二次请求的数据很快就客户端接收且正确渲染;紧接着第一次的数据也被客户端接收且客户端正确渲染。

这样就可能存在这样一种情况,第一次请求,服务端响应了请求但数据还没被客户端接收的时候,有人修改了数据;然后用户又点击刷新,响应数据 很快被客户端接收且处理,这个时候已经渲染的是最新的数据了。但是第一次请求的响应数据被客户端接收了,如果渲染的话,就不是最新的数据了。 因为第二次请求被成功处理后,第一次的请求就已经属于过期的了。

// 下拉选择器绑定的数据
const queryParams = reactive({
    keyword: ''
})

// 下拉列表渲染的数据
const listData = []

// 第几次搜索
let askNum = 0

// 每次搜索用的事件
const times = [5000, 100] // 第一次一共用了5s   第二次用了0.1s

// 监视书的信息
watch(queryParams, async (newV) => {
  // 3.查询参数第一次发生变化,响应很慢需要5s
  // 6.查询参数第二次发生变化,响应非常快

  asyncTaskIsExpired(times[askNum++], askNum)
    .then(() => {
      console.log(`第${askNum}个任务执行完毕`)
      // 渲染列表数据
      renderSelectData()
    })
})

/*
 * @param time // 任务需要的时间
 * @param count // 第几个任务
 */
function asyncTaskIsExpired(time, count) {
  return new Promise((resolve) => {
    console.log(`第${count}个任务开始了`)
    setTimeout(() => {
      resolve()
    }, time)
  })
}

const changeParams = function (str) {
  queryParams.keyword = str
}

function renderSelectData () {
    // do something
    console.log('渲染数据')
}

// 第一次搜索
changeBook('xxx')
// 第一次搜索过了两秒还没有返回数据
setTimeout(() => {
  // 就开始了第二次搜索
  changeBook('yyy')
}, 2000)

更新后的逻辑:

// watch的第三个参数,可以注册一个过期回调,当这个副作用函数的执行过期时将标识修改为true
// 换句话说,就是在watch内部每次检测到变化时,在副作用函数执行之前,会先执行通过onValidate注册的回调
watch(book, async (newV, oldV, onInvalidate) => {
  // 添加一个变量,标识上一次的请求是否过期
  let expired = false // 默认是不过期的
  onInvalidate(() => {
    // 过期时,将expired设置为true
    console.log('副作用函数已过期')
    expired = true
  })
  // asyncTask(times[askNum++], askNum)
  asyncTaskIsExpired(times[askNum++], askNum)
    .then(() => {
      if (!expired) {
        console.log(`第${askNum}个任务执行完毕`)
        renderSelectData()
      }
    })
})

更新后执行流程就是: 第一次搜索数据一直没有返回,用户就进行第二次搜索,第二次搜索很快就响应了,客户端在处理第二次搜索响应的数据之前,先将上一次的expired修改 为true,表示上一次请求已经过期,上一次请求即使成功(同时发起多个请求的时候),也不会执行后续操作了。

前面为什么说是“上一次的expired”呢,看上面代码,expired实际上是存在于一个闭包中的,因此修改expired不会影响到本次请求。

onInvalidate的实现

// 递归读取对象中的数据(让对象中的所有key都有对应副作用函数)
// 从而保证修改响应式对象的任意属性watch都能监听得到然后执行副作用函数
function traverse (value, seen = new Set()) {
    // 如果要读取的数据是原始值或者已经读取过了,那么什么都不做
    if (typeof value !== 'object' || value !== null || seen.has(value)) return
    // 将数据添加到seen中,待变遍历地读取过了,避免循环引用引起的死循环
    seen.add(value)
    // 假设value是一个对象,使用for-in读取对象的每一个值,并递归调用traverse进行处理
    for (const key in value) {
        traverse(value[k], seen)
    }
}

function watch (source, cb, options) {
    let getter
    if (typeof source === 'function') {
        getter = source
    } else {
        // 如果watch监听的是一个对象,读取对象的所有属性
        getter = () => traverse(source)
    }

    let oldValue, newValue
    // cleanup用来存储用户注册的过期回调
    let cleanup
    function onInvalidate(fn) {
        // 过期回调保存到cleanup中
        cleanup = fn
    }

    const job = () => {
        newValue = effectFn()
        // 调用回调函数cb之前,如果有国企的回调就先调用过期回调
        if (cleanup) {
            cleanup()
        }
        // 将 onInvalidate 作为回调函数的第三个参数,以边用户使用
        cb(newValue, oldValue, onInvalidate)
        oldValue = newValue
    }

    const effectFn = effect(
        () => getter,
        {
            lazy: true,
            scheduler: () => {
                if (options.flush === 'post') {
                    const p = new Promise()
                    p.then(job)
                } else {
                    job()
                }
            }
        }
    )
}

这里 onInvalidate 实际上起到传递过期回调的作用。将程序本身应该处理的逻辑加入到了vue中(类似插件系统)。

到此这篇关于Vue中watch清除过期副作用的案例详解的文章就介绍到这了,更多相关Vue watch清除过期副作用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue使用pdf-lib实现为文件流添加水印并预览

    Vue使用pdf-lib实现为文件流添加水印并预览

    这篇文章主要为大家详细介绍了Vue如何使用pdf-lib实现为文件流添加水印并预览的功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-03-03
  • 浅谈VUE防抖与节流的最佳解决方案(函数式组件)

    浅谈VUE防抖与节流的最佳解决方案(函数式组件)

    这篇文章主要介绍了浅谈VUE防抖与节流的最佳解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • Vue 动态生成数据字段的实例

    Vue 动态生成数据字段的实例

    这篇文章主要介绍了Vue 动态生成数据字段的实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue父组件向子组件传递多个数据的实例

    vue父组件向子组件传递多个数据的实例

    下面小编就为大家分享一篇vue父组件向子组件传递多个数据的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • vue element-ui中table合计指定列求和实例

    vue element-ui中table合计指定列求和实例

    这篇文章主要介绍了vue element-ui中table合计指定列求和实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • Vue动态权限登录实现(基于路由与角色)

    Vue动态权限登录实现(基于路由与角色)

    很多应用都会需要对不同的用户进行权限控制,本文主要介绍了Vue动态权限登录实现(基于路由与角色),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • 在Vue项目中,防止页面被缩放和放大示例

    在Vue项目中,防止页面被缩放和放大示例

    今天小编就为大家分享一篇在Vue项目中,防止页面被缩放和放大示例,具有很好的参考 价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-10-10
  • vue实现各种文件文档下载及导出示例

    vue实现各种文件文档下载及导出示例

    这篇文章主要介绍了vue实现各种文件文档下载及导出示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • 前端elementUI select选择器实现远程搜索

    前端elementUI select选择器实现远程搜索

    这篇文章主要为大家介绍了前端使用elementUI select选择器实现远程搜索,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • 前端预览pdf文件流详细步骤

    前端预览pdf文件流详细步骤

    公司最近有这么个需求,在线预览pdf功能,正好有点时间,稍微整理记录一下,这篇文章主要给大家介绍了关于前端预览pdf文件流的相关资料,需要的朋友可以参考下
    2023-09-09

最新评论