vue3调度器scheduler功能和用法详解

 更新时间:2023年06月05日 10:02:27   作者:包子爱吃肉  
调度器是vue3响应式系统中一个非常重要的特性,可调度性指的是当trigger 动作触发副作用函数重新执行时,有能力决定副作用函数执行的时机、次数以及方式,本文通过代码示例给大家介绍调度器是什么,有什么功能,怎么使用,感兴趣的同学可以借鉴阅读

调度器是vue3响应式系统中一个非常重要的特性,可调度性指的是当trigger 动作触发副作用函数重新执行时,有能力决定副作用函数执行的时机、次数以及方式

const data = { foo: 1 }
const obj = new Proxy(data, { /* ... */ })    // 上文中的响应式
 effect(() => {
 console.log(obj.foo)
 })
 obj.foo++
 console.log('结束了')

正常执行结果顺序是1,2,结束了,但是,若我们期望的打印顺序发生改变1,结束了,2,要实现这样的打印结果,就需要使用到调度器

  • 可以为函数effect函数设计一个选项参数options,允许用户指定调度器:
    effect(
        () => {
          console.log(obj.foo);
        },
        // options
        {
          // 调度器 scheduler 是一个函数
          scheduler(fn) { 
            // ... 
         },
        });
  • 将调度器对象挂在到当前副作用函数中
    // effect 函数用于注册副作用函数
      function effect(fn, options = {}) {
        fn.options = options; // 新增挂在调度器
        activeEffect = fn;
        // 执行副作用函数
        fn();
      }
  • 修改参数时,判断是否存在调度器,存在,执行当前挂载中调度器方法(在taigger函数中)
   function trigger(target, key) {
        // 根据target从桶中取得depsMap,它是key --> effects
        const depsMap = bucket.get(target);
        if (!depsMap) return;
        // 根据key取得当前对应的副作用函数
        const effects = depsMap.get(key);
        // 执行副作用函数
        effects && effects.forEach((fn) => {
        // fn()
            if (fn.options.scheduler) {    // 新增
              fn.options.scheduler(fn);
            } else {
              // 否则直接执行副作用函数(之前的默认行为)
              fn();
            }
          });
      }
  • 使用 setTimeout 开启一个宏任务来执行副作用函数 fn,这样,就能改变执行顺序,拿到我们想要的执行结果
 function effect(fn, options = {}) {
        fn.options = options; // 新增
        activeEffect = fn;
        // 执行副作用函数
        fn();
      }
      effect(
        () => {
          console.log(obj.foo);
        },
        // options
        {
          // 调度器 scheduler 是一个函数
          scheduler(fn) {
            // 修改参数,将副作用函数放在宏任务队列中执行
            setTimeout(fn)
          },
        }
      );

完整代码:

 const data = { foo: 1 };
      // 用一个全局变量存储被注册的副作用函数
      let activeEffect;
      // 创建一个新桶来存储副作用函数,包含key和value
      const bucket = new WeakMap();
      const obj = new Proxy(data, {
        get(target, key) {
          // target:当前对象,key:触发监听的key
          track(target, key);
          return target[key];
        },
        set(target, key, newVal) {
          target[key] = newVal;
          trigger(target, key);
        },
      });
      // track函数
      function track(target, key) {
        // 没有正在执行的副作用函数 直接返回
        if (!activeEffect) return target[key];
        // 从这个桶中取出一个Map类型(key -> value)
        let depsMap = bucket.get(target);
        // 不存在,则创建一个Map与target关联
        if (!depsMap) {
          bucket.set(target, (depsMap = new Map()));
        }
        // 根据key判断每个key上是否存在对应的副作用函数
        let deps = depsMap.get(key);
        // 不存在,则新建一个new Set,并与key关联
        if (!deps) {
          depsMap.set(key, (deps = new Set()));
        }
        // 最后将当前激活的副作用函数添加到桶中
        deps.add(activeEffect);
      }
      // trigger函数
      function trigger(target, key) {
        // 根据target从桶中取得depsMap,它是key --> effects
        const depsMap = bucket.get(target);
        if (!depsMap) return;
        // 根据key取得当前对应的副作用函数
        const effects = depsMap.get(key);
        // 执行副作用函数
        effects &&
          effects.forEach((fn) => {
            if (fn.options.scheduler) {
              fn.options.scheduler(fn);
            } else {
              // 否则直接执行副作用函数(之前的默认行为)
              fn();
            }
          });
      }
      // effect 函数用于注册副作用函数
      function effect(fn, options = {}) {
        fn.options = options; // 新增
        activeEffect = fn;
        // 执行副作用函数
        fn();
      }
      effect(
        () => {
          console.log(obj.foo);
        },
        // options
        {
          // 调度器 scheduler 是一个函数
          scheduler(fn) {
            // 修改参数,将副作用函数放在宏任务队列中执行
            setTimeout(fn);
          },
        }
      );
      obj.foo++;
      console.log("结束了");

除了控制副作用函数的执行顺序,通过调度器还可以做到控制它的执行次数

  • 正常的打印结果应该是1,2,3,但是我们只关心执行的最后结果,应该拿到的是1,3,执行三次有些多余,这时,就需要使用到调度器来修改执行次数
const data = { foo: 1 }
const obj = new Proxy(data, { /* ... */ })    // 上文中的响应式
 effect(() => {
 console.log(obj.foo)
 })
 obj.foo++
 obj.foo++;
  • 实现不包含过渡阶段,使用调度器基于promise,可以直接修改当前当前调度函数
    // 定义一个任务队列,使用它自动去重能力
      const jobQueue = new Set();
      // 使用 Promise.resolve() 创建一个 promise 实例,我们用它将一个任务添加到微任务队列
      const p = Promise.resolve();
      // 一个标志代表是否正在刷新队列
      let isFlushing = false;
      function flushJob() {
        // 如果队列正在刷新,则什么都不做
        if (isFlushing) return;
        // 设置为 true,代表正在刷新
        isFlushing = true;
        // 在微任务队列中刷新 jobQueue 队列
        p.then(() => {
          jobQueue.forEach((job) => job());
        }).finally(() => {
          // 结束后重置 isFlushing
          isFlushing = false;
        });
      }
  • 执行调度函数
   effect(
        () => {
          console.log(obj.foo);
        },
        // options
        {
          // 调度器 scheduler 是一个函数
          scheduler(fn) {
            jobQueue.add(fn);
            // 调用 flushJob 刷新队列
            flushJob();
          },
        }
      );
      obj.foo++;
      obj.foo++;

到此这篇关于vue3调度器scheduler功能和用法详解的文章就介绍到这了,更多相关vue3调度器scheduler内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue-admin-template 报Uncaught (in promise) error问题及解决

    Vue-admin-template 报Uncaught (in promise) error问题及解决

    这篇文章主要介绍了Vue-admin-template 报Uncaught (in promise) error问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • element-ui之关于组件BackToTop回到顶部的使用

    element-ui之关于组件BackToTop回到顶部的使用

    这篇文章主要介绍了element-ui之关于组件BackToTop回到顶部的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • 详解Vue 事件修饰符capture 的使用

    详解Vue 事件修饰符capture 的使用

    capture事件修饰符的作用是给元素添加一个监听器,当元素发生冒泡时,先触发带有该修饰符的元素。这篇文章给大家介绍了Vue 事件修饰符capture 的使用,需要的朋友参考下吧
    2017-12-12
  • Vue3+Canvas实现坦克大战游戏(一)

    Vue3+Canvas实现坦克大战游戏(一)

    这篇文章将利用Vue3和Canvas编写一个童年经典游戏—坦克大战,文中的示例代码讲解详细,感兴趣的小伙伴快来跟随小编一起学习一下吧
    2022-03-03
  • vite.config.js配置入门详解

    vite.config.js配置入门详解

    本文主要介绍了vite.config.js配置入门详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 完美解决通过IP地址访问VUE项目的问题

    完美解决通过IP地址访问VUE项目的问题

    这篇文章主要介绍了完美解决通过IP地址访问VUE项目的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • 在vue中使用css modules替代scroped的方法

    在vue中使用css modules替代scroped的方法

    本篇文章主要介绍了在vue中使用css modules替代scroped的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • vue2模拟vue-element-admin手写角色权限的实现

    vue2模拟vue-element-admin手写角色权限的实现

    本文主要介绍了vue2模拟vue-element-admin手写角色权限的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Vue简明介绍配置对象的配置选项

    Vue简明介绍配置对象的配置选项

    我们知道每一个vue项目应用都是通过vue的构造函数进行创建一个新的vue项目的。创建vue实例的配置对象,可以包括一下属性选项,比如:data、methods、watch、template等等,每一个选项都有不同的功能,大家可以根据自己的需求选择不同的配置
    2022-08-08
  • vue-pdf插件实现pdf文档预览方式(自动分页预览)

    vue-pdf插件实现pdf文档预览方式(自动分页预览)

    这篇文章主要介绍了vue-pdf插件实现pdf文档预览方式(自动分页预览),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03

最新评论