Vue中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清除过期副作用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue中watch使用方法详解

    目录 前言 watch immediate和handler deep 拓展 前言 说到 vue 中的 watch 方法,大家可能首先想到,它是用来监听数据的变化,一旦数据发生变化可以执行一些其他的操作.但是 watch 的操作可不止如此,本章就带大家一起深剖细析 vue 中的 watch 方法. watch 因为 vue 是双向数据绑定,所以当页面数据发生变化时,我们通过 watch 方法就可以拿到数据变化前和变化后的值,从而做一系列操作,下面我们通过一个简单的例子来解释. 先看下面这段代码 <

  • Vue watch监听使用的几种方法

    目录 一.watch使用的几种方法 二.watch中的immediate与handler和deep属性 一.watch使用的几种方法 1. 通过 watch 监听 msg 数据的变化. watch: { msg(oldValue, newValue) { console.log(oldValue) console.log(newValue) } } 2.通过 watch 监听 obj数据的变化.(深度监听 deep) data() { return { obj: { 'name': "赵&quo

  • vue中watch监听不到变化的解决

    目录 watch监听不到对象内部的变化 解决方法: watch的handler方法的两个参数值相同 解决方法: 全部代码 watch监听不到对象内部的变化 有的时候vue会出现这种现象,无法监听到复杂对象内部的变化:当对象里面原本有某一个属性,并对这个属性操作时,watch是可以监听到当前属性的变化的.但是,若对象里面本没有这个属性的时候,在操作时将属性添加进去,同时包括之后对这个属性的操作,watch是都检测不到的.这是因为vue的watch会在初始化的时候通过object.definePro

  • Vue3中watch的使用详解

    目录 Vue3中watch的详解 Vue2使用watch Vue3使用watch 情况1 情况2 情况3 情况4 情况5 特殊情况 Vue3中watch的详解 Vue2使用watch <template> <div>总合:{{ sum }}<button @click="sum++">点击累加</button></div> </template> <script> import { ref } from

  • Vue不能watch数组和对象变化解决方案

    目录 Vue 能监听数组的情况 Vue 无法监听数组变化 举例无法监听数组变化的情况 Vue 无法监听数组变化的解决方案 Vue 监听对象 Vue 设置监听对象 Vue 能监听数组的情况 Vue 监听数组和对象的变化(vue2.x) vue 实际上可以监听数组变化,比如: 直接 = 赋值 data () { return { watchArr: [], }; }, watch: { watchArr (newVal) { console.log('监听:' + newVal); } }, cre

  • Vue watch原理源码层深入讲解

    目录 添加依赖 触发依赖 总结 由于我在从源码看vue(v2.7.10)的computed的实现原理中详细的讲解过computed的实现,本篇跟computed的原理类似.我就带大家简单分析一下. 添加依赖 代码如下: <template> <div> {{a}} <button @click="addModule">新增</button> </div> </template> <script> exp

  • vue中watch的实际开发学习笔记

    目录 前言 一.watch是什么 二.watch不同的实际使用场景开发案例 1.场景1:加载完成后登录监听 2.场景2:前面不同的选择框,影响下面的不同的选择框的选项内容 3.场景3:深度监听 4.场景4:赋值使用 5.场景5:改变标记的true和false 6.场景6:页面的跳转 补充:watch简写形式 总结 前言 本文主要是积累一下在使用前端的watch开发过程中遇到的问题点和经验. 一.watch是什么 根据本人的理解,它就是一个监听器,就是说监听的某个数据发生了变化,那么它就会执行相对

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

    在这里就不过多说watch的用法了,主要了解一下如何清除过期的副作用 通过一个案例来说吧: 一个可搜索的下拉选择器,用户第一次进行搜索的时候网速比较慢,请求虽然被服务端正确响应了,但是数据一直没有传输到客户端,用户看下拉数据没变化 就第二次搜索.第二次搜索之后网速恢复正常了,第二次请求的数据很快就客户端接收且正确渲染:紧接着第一次的数据也被客户端接收且客户端正确渲染. 这样就可能存在这样一种情况,第一次请求,服务端响应了请求但数据还没被客户端接收的时候,有人修改了数据:然后用户又点击刷新,响应数

  • Vue 过渡(动画)transition组件案例详解

    Vue过度(动画),本质走的是CSS3:transtion,animation. 控制器div显示/隐藏,代码如下: <div id="box"> <input type="button" value="按钮" @click="toggle"> <div id="div1" v-show="isShow"></div> </div&g

  • Vue之使用mockjs生成模拟数据案例详解

    目录 在项目中安装mockjs 在Vue项目中使用mockjs的基本流程 Mock语法规范 数据模板定义规范(Data Template Definition,DTD) 数据占位符定义规范(Data Placeholder Definition,DPD) Mock.mock() Mock.Random() 在项目中安装mockjs 在项目目录下执行以下安装命令 npm install mockjs --save 在Vue项目中使用mockjs的基本流程 安装完成后,在项目src/utils目录下

  • Vue子组件调用父组件方法案例详解

    一.直接在子组件中通过this.$parent.event来调用父组件的方法 <!-- 父组件 --> <template> <div> <child></child> </div> </template> <script> import child from '~/components/dam/child'; export default { components: { child }, methods: {

  • Vue 中的 computed 和 watch 的区别详解

    目录 computed 注意 应用场景 watch 总结 computed computed 看上去是方法,但是实际上是计算属性,它会根据你所依赖的数据动态显示新的计算结果.计算结果会被缓存,computed 的值在 getter 执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取 computed 的值时才会重新调用对应的 getter 来计算. 1)下面是一个比较经典简单的案例 <template> <div class="hello"> {{ful

  • vue中使用mxgraph的方法实例代码详解

    1.npm 引入 npm install mxgraph --save 2.这个模块可以使用require()方法进行加载.它将返回一个接受对象作为选项的工厂函数.必须将mxBasePath选项提供给工厂函数,而不是将其定义为一个全局变量. var mxgraph = require("mxgraph")( { // 以下地址不需要修改 mxImageBasePath: "./src/images", mxBasePath: "./src" })

  • vue中datepicker的使用教程实例代码详解

    写这个文章主要是记录下用法,官网已经说的很详细了 npm install vue-datepicker --save html代码 <myDatepicker :date="startTime" :option="multiOption" :limit="limit"></myDatepicker> <myDatepicker :date="endtime" :option="timeo

  • Vue中遍历数组的新方法实例详解

    1.foreach foreach循环对不能使用return来停止循环 search(keyword){ var newList = [] this.urls.forEach(item =>{ if(item.name.indexOf(keyword) != -1){ newList.push(item) } }) return newList } 2.filter item对象就是遍历数组中的一个元素,includes是es6中的新方法,在search方法中直接返回新数组 search(key

  • Vue中使用webpack别名的方法实例详解

    在工作中,我们经常会写出这种代码: import MHeader from '../../components/m-header/m-header' @import "../../common/stylus/variable" @import "../../common/stylus/mixin" 即,需要引入公共文件,但是公共文件的文件路径里当前文件很远,那么就会形成上面示例中的那种路径很长的情况. 而因为文件目录是约定俗成的,不可轻易更改,无法修改相对路径.那么

  • vue中{{}},v-text和v-html区别与应用详解

    {{}}获取值,不会清空标签原有内容 v-text 获取值,会清空标签原有内容,输出的是纯文本 v-html 获取值,会清空标签原有内容,若数据中包含html标签,将其当html标签解析后输出 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <

随机推荐