vue3响应式实现原理(3)

简介: vue3响应式实现原理(3)

theme: fancy

highlight: a11y-light

纠正两个问题

function trigger(target, key) {
   
  let effects = bucket?.get(target)?.get(key);
  const effectsToRun = new Set(effects);
  effects &&
    effectsToRun.forEach((effectFn) => {
   
    //开始
      let deps = effectFn.deps;
      deps.forEach((item) => {
   
        item.delete(effectFn);
      });
      effectFn.deps.length = 0;
      //结尾
      //这一段代码应放到执行effectFn函数之前,如果放在这,后面的代码不需要执行,effectFn函数也不会触发,所以需要把这段代码换个位置
      if (activeEffect !== effectFn) {
   
        effectFn.options.scheduler
          ? effectFn.options.scheduler(effectFn)
          : effectFn();
      }
    });
}
//放到effect函数内
function effect(fn, options = {
   }) {
   
  const effectFn = () => {
   
    let deps = effectFn.deps;
    deps.forEach((item) => {
   
      item.delete(effectFn);
    });
    effectFn.deps.length = 0;
    activeEffect = effectFn;
    effectStack.push(activeEffect);
    res=fn();//也就是以这个函数执行为分界线,在这之前清除副作用,赋值入栈,之后出栈赋值
    effectStack.pop();
    activeEffect = effectStack[effectStack.length - 1];
    //return fn();不能直接返回fn()会导致fn的立即执行
    return res//用res存下来
  };
  effectFn.deps = [];
  effectFn.options = options;
  if (options.lazy) {
   
    return effectFn;
  } else {
   
    effectFn();
  }
}

实现watch

首先说明watch的实现也需要effect加调度器,先实现一个基础的watch

function watch(obj, cb) {
   
  effect(() => obj.age, {
   
    scheduler() {
   
      cb();
    },
  });
}
watch(proxyData, () => {
   
  console.log("changed");
});
proxyData.age++;
proxyData.age++;

上面只能监听一个属性,现在需要监听传入对象的任意属性都能触发

function watch(obj, cb) {
   
  effect(
    () => {
   
      travel(obj)
    },
    {
   
      scheduler() {
   
        cb();
      },
    }
  );
}
function travel(obj){
   
  for (const key in obj) {
   
    if (typeof obj[key] === 'object' && obj[key]!=null) {
   
      travel(obj[key]);
    }
  }
}

watch的参数也能传入一个函数,实现也很简单,只需要简单判断执行

function watch(getter, cb) {
   
  effect(
    () => {
   
      if (typeof getter === "function") {
   
        getter();
      } else {
   
        travel(getter);
      }
    },
    {
   
      scheduler() {
   
        cb();
      },
    }
  );
}

那么怎么拿到watch的新值和旧值,可以借助之前的lazy,他保存着上次副作用函数执行的结果,也就是我们需要的旧值,并且watch不需要再执行副作用函数,所以使用lazy没有任何影响。

function watch(getter, cb) {
   
  let newVal, oldVal;//新增
  const effectFn = effect(
    () => {
   
      if (typeof getter === "function") {
   
        return getter();//新增,这里要加个return,把值返回回来才能用lazy拿到
      } else {
   
        travel(getter);
      }
    },
    {
   
      lazy: true,//新增
      scheduler() {
   
        newVal = effectFn();//新增
        cb(newVal, oldVal);
        oldVal = newVal;//新增
      },
    }
  );
  oldVal = effectFn();//新增
}

watch还有一个参数immediate,在注册的时候就应该监听一次,之前在注册watch函数后等待数据改变触发副作用,现在直接在注册watch函数手动执行一次函数。实现:给watch添加第三个参数

watch(
  () => proxyData.age,
  (newVal, oldVal) => {
   
    console.log(newVal, oldVal);
  },
  {
    immediate: true }
);

function watch(getter, cb, options = {
   }) {
   
  let newVal, oldVal;
  const job = () => {
   //将调度函数抽离
    newVal = effectFn();
    cb(newVal, oldVal);
    oldVal = newVal;
  };
  const effectFn = effect(
    () => {
   
      if (typeof getter === "function") {
   
        return getter();
      } else {
   
        travel(getter);
      }
    },
    {
   
      lazy: true,
      scheduler: job,//修改
    }
  );
  if (options.immediate) {
   //新增
    job();
  } else {
   
    newVal = effectFn();
  }
}

flush用于控制调度器的执行时机

在调度器函数内检测 options.flush 的值是否为post,如果是,则将 job 函数放到微任务队列中,从而实现异步延迟执行;否则直接执行 job 函数

watch(
  () => proxyData.age,
  (newVal, oldVal) => {
   
    console.log(newVal, oldVal);
  },
  {
    immediate: true, flush: "post" }
);

function watch(getter, cb, options = {
   }) {
   
  let newVal, oldVal;
  const job = () => {
   
    newVal = effectFn();
    cb(newVal, oldVal);
    oldVal = newVal;
  };
  const effectFn = effect(
    () => {
   
      if (typeof getter === "function") {
   
        return getter();
      } else {
   
        travel(getter);
      }
    },
    {
   
      lazy: true,
      scheduler() {
   //修改
        if (options.flush === "post") {
   
          const p = Promise.resolve();
          p.then(job);
        } else {
   
          job();
        }
      },
    }
  );
  if (options.immediate) {
   
    job();
  } else {
   
    newVal = effectFn();
  }
}
相关文章
|
7天前
|
JavaScript 前端开发 UED
vue2和vue3的响应式原理有何不同?
大家好,我是V哥。本文详细对比了Vue 2与Vue 3的响应式原理:Vue 2基于`Object.defineProperty()`,适合小型项目但存在性能瓶颈;Vue 3采用`Proxy`,大幅优化初始化、更新性能及内存占用,更高效稳定。此外,我建议前端开发者关注鸿蒙趋势,2025年将是国产化替代关键期,推荐《鸿蒙 HarmonyOS 开发之路》卷1助你入行。老项目用Vue 2?不妨升级到Vue 3,提升用户体验!关注V哥爱编程,全栈开发轻松上手。
|
10天前
|
JavaScript 前端开发 算法
高效工作流:用Mermaid绘制你的专属流程图;如何在Vue3中导入mermaid绘制流程图
mermaid是一款非常优秀的基于 JavaScript 的图表绘制工具,可渲染 Markdown 启发的文本定义以动态创建和修改图表。非常适合新手学习或者做一些弱交互且自定义要求不高的图表 除了流程图以外,mermaid还支持序列图、类图、状态图、实体关系图等图表可供探索。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
10天前
|
JavaScript 前端开发 API
你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解
onMounted作为vue3中最常用的钩子函数之一,能够灵活、随心应手的使用是每个Vue开发者的必修课,同时根据其不同写法的特性,来选择最合适最有利于维护的写法。博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
20天前
|
资源调度 JavaScript 前端开发
Pinia 如何在 Vue 3 项目中进行安装和配置?
Pinia 如何在 Vue 3 项目中进行安装和配置?
|
10天前
|
JavaScript 前端开发 API
管理数据必备;侦听器watch用法详解,vue2与vue3中watch的变化与差异
一篇文章同时搞定Vue2和Vue3的侦听器,是不是很棒?不要忘了Vue3中多了一个可选项watchEffect噢。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
4月前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
139 56
|
2月前
|
资源调度 JavaScript 前端开发
创建vue3项目步骤以及安装第三方插件步骤【保姆级教程】
这是一篇关于创建Vue项目的详细指南,涵盖从环境搭建到项目部署的全过程。
332 1
|
3月前
|
JavaScript API 数据处理
vue3使用pinia中的actions,需要调用接口的话
通过上述步骤,您可以在Vue 3中使用Pinia和actions来管理状态并调用API接口。Pinia的简洁设计使得状态管理和异步操作更加直观和易于维护。无论是安装配置、创建Store还是在组件中使用Store,都能轻松实现高效的状态管理和数据处理。
191 3
|
10月前
|
JavaScript API
【vue实战项目】通用管理系统:api封装、404页
【vue实战项目】通用管理系统:api封装、404页
102 3
|
10月前
|
人工智能 JavaScript 前端开发
毕设项目-基于Springboot和Vue实现蛋糕商城系统(三)
毕设项目-基于Springboot和Vue实现蛋糕商城系统
115 0

相关实验场景

更多