广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue3 响应式系统实现 computed
  • 689
分享到

Vue3 响应式系统实现 computed

2024-04-02 19:04:59 689人浏览 安东尼
摘要

目录前言实现 computed总结前言 上篇文章我们实现了基本的响应式系统,这篇文章继续实现 computed。 首先,我们简单回顾一下: 响应式系统的核心就是一个 WeakMap

前言

上篇文章我们实现了基本的响应式系统,这篇文章继续实现 computed。

首先,我们简单回顾一下:

响应式系统的核心就是一个 WeakMap --- Map --- Set 的数据结构

WeakMap 的 key 是原对象,value 是响应式的 Map。这样当对象销毁的时候,对应的 Map 也会销毁。

Map 的 key 就是对象的每个属性,value 是依赖这个对象属性的 effect 函数的集合 Set。然后用 Proxy 代理对象的 get 方法,收集依赖该对象属性的 effect 函数到对应 key 的 Set 中。还要代理对象的 set 方法,修改对象属性的时候调用所有该 key 的 effect 函数。

上篇文章我们按照这样的思路实现了一个比较完善的响应式系统,然后今天继续实现 computed。

实现 computed

首先,我们把之前的代码重构一下,把依赖收集和触发依赖函数的执行抽离成 track 和 trigger 函数:

逻辑还是添加 effect 到对应的 Set,以及触发对应 Set 里的 effect 函数执行,但抽离出来清晰多了。

然后继续实现 computed。

computed 的使用大概是这样的:

const value = computed(() => {
    return obj.a + obj.b;
});

对比下 effect:

effect(() => {
    console.log(obj.a);
});

区别只是多了个返回值。

所以我们基于 effect 实现 computed 就是这样的:

function computed(fn) {
    const value = effect(fn);
   return value
}

当然,现在的 effect 是没有返回值的,要给它加一下:

只是在之前执行 effect 函数的基础上把返回值记录下来返回,这个改造还是很容易的。

现在 computed 就能返回计算后的值了:

但是现在数据一遍,所有的 effect 都执行了,而像 computed 这里的 effect 是没必要每次都重新执行的,只需要在数据变了之后执行。

所以我们添加一个 lazy 的 option 来控制 effect 不立刻执行,而是把函数返回让用户自己执行。

然后 computed 里用 effect 的时候就添加一个 lazy 的 option,让 effect 函数不执行,而是返回出来。

computed 里创建一个对象,在 value 的 get 触发时调用该函数拿到最新的值:

我们测试下:

可以看到现在 computed 返回值的 value 属性是能拿到计算后的值的,并且修改了 obj.a. 之后会重新执行计算函数,再次拿 value 时能拿到新的值。

只是多执行了一次计算,这是因为 obj.a 变的时候会执行所有的 effect 函数:

这样每次数据变了都会重新执行 computed 的函数来计算最新的值。

这是没有必要的,effect 的函数是否执行应该也是可以控制的。所以我们要给它加上调度的功能:

可以支持传入 schduler 回调函数,然后执行 effect 的时候,如果有 scheduler 就传给它让用户自己来调度,否则才执行 effect 函数。

这样用户就可以自己控制 effect 函数的执行了:

然后再试一下刚才的代码:

可以看到,obj.a 变了之后并没有执行 effect 函数来重新计算,因为我们加了 sheduler 来自己调度。这样就避免了数据变了以后马上执行 computed 函数,可以自己控制执行。

现在还有一个问题,每次访问 res.value 都要计算:

能不能加个缓存呢?只有数据变了才需要计算,否则直接拿之前计算的值。

当然是可以的,加个标记就行:

scheduler 被调用的时候就说明数据变了,这时候 dirty 设置为 true,然后取 value 的时候就重新计算,之后再改为 false,下次取 value 就直接拿计算好的值了。

我们测试下:

我们访问 computed 值的 value 属性时,第一次会重新计算,后面就直接拿计算好的值了。

修改它依赖的数据后,再次访问 value 属性会再次重新计算,然后后面再访问就又会直接拿计算好的值了。

至此,我们完成了 computed 的功能。

但现在的 computed 实现还有一个问题,比如这样一段代码:

let res = computed(() => {
    return obj.a + obj.b;
});
effect(() => {
    console.log(res.value);
});

我们在一个 effect 函数里用到了 computed 值,按理说 obj.a 变了,那 computed 的值也会变,应该触发所有的 effect 函数。

但实际上并没有:

这是为什么呢?

这是因为返回的 computed 值并不是一个响应式的对象,需要把它变为响应式的,也就是 get 的时候 track 收集依赖,set 的时候触发依赖的执行:

我们再试一下:

现在 computed 值变了就能触发依赖它的 effect 了。至此,我们的 computed 就很完善了。

完整代码如下:

const data = {
    a: 1,
    b: 2
}
let activeEffect
const effectStack = [];
function effect(fn, options = {}) {
  const effectFn = () => {
      cleanup(effectFn)
      activeEffect = effectFn
      effectStack.push(effectFn);
      const res = fn()
      effectStack.pop()
      activeEffect = effectStack[effectStack.length - 1]
      return res
  }
  effectFn.deps = []
  effectFn.options = options;
  if (!options.lazy) {
    effectFn()
  }
  return effectFn
}
function computed(fn) {
    let value
    let dirty = true
    const effectFn = effect(fn, {
        lazy: true,
        scheduler(fn) {
            if(!dirty) {
                dirty = true
                trigger(obj, 'value');
            }
        }
    });
    const obj = {
        get value() {
            if (dirty) {
                value = effectFn()
                dirty = false
            }
            track(obj, 'value');
            console.log(obj);
            return value
        }
    }
    return obj
}
function cleanup(effectFn) {
    for (let i = 0; i < effectFn.deps.length; i++) {
        const deps = effectFn.deps[i]
        deps.delete(effectFn)
    }
    effectFn.deps.length = 0
}
const ReactiveMap = new WeakMap()
const obj = new Proxy(data, {
    get(targetObj, key) {
        track(targetObj, key);

        return targetObj[key]
   },
   set(targetObj, key, newVal) {
        targetObj[key] = newVal

        trigger(targetObj, key)
    }
})
function track(targetObj, key) {
    let depsMap = reactiveMap.get(targetObj)
    if (!depsMap) {
    reactiveMap.set(targetObj, (depsMap = new Map()))
    }
    let deps = depsMap.get(key)
    if (!deps) {
    depsMap.set(key, (deps = new Set()))
    }
    deps.add(activeEffect)
    activeEffect.deps.push(deps);
}
function trigger(targetObj, key) {
    const depsMap = reactiveMap.get(targetObj)
    if (!depsMap) return
    const effects = depsMap.get(key)
    const effectsToRun = new Set(effects)
    effectsToRun.forEach(effectFn => {
        if(effectFn.options.scheduler) {
            effectFn.options.scheduler(effectFn)
        } else {
            effectFn()
        }
    })
}

总结

上篇文章我们实现了响应式的核心数据结构,依赖的收集、数据变化后通知依赖函数执行。今天我们在那基础上实现了 computed。我们改造了 effect 函数,让它返回传入的 fn,然后在 computed 里自己执行来拿到计算后的值。

我们又支持了 lazy 和 scheduler 的 option,lazy 是让 effect 不立刻执行传入的函数,scheduler 是在数据变动触发依赖执行的时候回调 sheduler 来调度。

我们通过标记是否 dirty 来实现缓存,当 sheduler 执行的时候,说明数据变了,把 dirty 置为 true,重新计算 computed 的值,否则直接拿缓存。此外,computed 的 value 并不是响应式对象,我们需要单独的调用下 track 和 trigger。这样,我们就实现了完善的 computed 功能,vue3 内部也是这样实现的。

到此这篇关于 Vue3 响应式系统实现 computed的文章就介绍到这了,更多相关 Vue3 computed内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Vue3 响应式系统实现 computed

本文链接: https://www.lsjlt.com/news/153113.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue3 响应式系统实现 computed
    目录前言实现 computed总结前言 上篇文章我们实现了基本的响应式系统,这篇文章继续实现 computed。 首先,我们简单回顾一下: 响应式系统的核心就是一个 WeakMap ...
    99+
    2022-11-13
  • Vue3响应式系统怎么实现computed
    首先,我们简单回顾一下:响应式系统的核心就是一个 WeakMap --- Map --- Set 的数据结构。WeakMap 的 key 是原对象,value 是响应式的 Map。这样当对象销毁的时候,对应的 Map 也会销毁。Map 的 ...
    99+
    2023-05-15
    Vue3 computed
  • Vue3响应式系统如何实现computed
    这篇“Vue3响应式系统如何实现computed”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue3响应式系统如何实现co...
    99+
    2023-07-06
  • 怎么手写Vue3响应式系统
    这篇文章主要介绍了怎么手写Vue3响应式系统的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么手写Vue3响应式系统文章都会有所收获,下面我们一起来看看吧。响应式首先,什么是响应式呢?响应式就是被观察的数据变化...
    99+
    2023-07-02
  • 如何手写Vue3响应式系统
    这篇文章主要介绍“如何手写Vue3响应式系统”,在日常操作中,相信很多人在如何手写Vue3响应式系统问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何手写Vue3响应式系统”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-07-06
  • 详解vue3 响应式的实现原理
    目录核心设计思想Vue.js 2.x 响应式Vue.js 3.x 响应式依赖收集:get 函数派发通知:set 函数总结源码参考核心设计思想 除了组件化,Vue.js 另一个核心设计...
    99+
    2022-11-13
  • setup+ref+reactive实现vue3响应式功能
    setup 是用来写组合式 api ,内部的数据和方法需要通过 return 之后,模板才能使用。在之前 vue2 中,data 返回的数据,可以直接进行双向绑定使用,如果我们把 s...
    99+
    2022-11-12
  • Vue3的响应式机制怎么实现
    这篇文章主要介绍了Vue3的响应式机制怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue3的响应式机制怎么实现文章都会有所收获,下面我们一起来看看吧。一、什么是响应式?在javascript中的变量是...
    99+
    2023-07-04
  • Vue2响应式系统之深度响应怎么实现
    本文小编为大家详细介绍“Vue2响应式系统之深度响应怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue2响应式系统之深度响应怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1、场景import&...
    99+
    2023-06-30
  • Vue3响应式对象是如何实现的(1)
    目录简单的响应式实现Proxy与响应式为什么需要Proxy?Proxy创建的代理对象与原始对象有何不同?多副作用函数的响应式实现简单的响应式实现 为了方便说明,先来看一个简单的例子。...
    99+
    2022-11-13
  • Vue3响应式对象是如何实现的(2)
    目录前言分支切换的优化副作用函数嵌套产生的BUG自增/自减操作产生的BUG前言 在Vue3响应式对象是如何实现的(1)中,我们已经从功能上实现了一个响应式对象。如果仅仅满足于功能实现...
    99+
    2022-11-13
  • vue3响应式实现readonly的方法是什么
    readonly的实现it("happy path", () => { console.warn = vi.fn(); const original = { foo: 1, };...
    99+
    2023-05-21
    Vue3 readonly
  • vue3响应式实现readonly从零开始教程
    目录前言readonly的实现重构结束前言 前面的章节我们把 effect 部分大致讲完了,这部分我们来讲 readonly以及重构一下reactive。 readonly的实现 ...
    99+
    2023-03-06
    vue3响应式readonly vue readonly
  • setup+ref+reactive如何实现vue3响应式功能
    这篇文章给大家介绍setup+ref+reactive如何实现vue3响应式功能,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。setup 是用来写组合式 api ,内部的数据和方法需要通过 return 之后,模板才能...
    99+
    2023-06-21
  • 使用Java怎么实现响应式系统
    使用Java怎么实现响应式系统?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。初识响应式系统ReactiveX的本质就是Observer+Iterator+函数编程+异步。是一个...
    99+
    2023-06-15
  • Vue3响应式原理实例分析
    本篇内容介绍了“Vue3响应式原理实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!回顾 vue2.x 的响应式实现原理:对象类型:通过...
    99+
    2023-07-02
  • 手写 Vue3 响应式系统(核心就一个数据结构)
    目录前言响应式总结前言 响应式是 Vue 的特色,如果你简历里写了 Vue 项目,那基本都会问响应式实现原理。而且不只是 Vue,状态管理库 Mobx 也是基于响应式实现的。那响应式...
    99+
    2022-11-13
  • defineProperty和Proxy如何实现的响应式系统
    这篇文章主要介绍了defineProperty和Proxy如何实现的响应式系统,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、极简双向绑定...
    99+
    2022-10-19
  • Vue2响应式系统之嵌套怎么实现
    这篇“Vue2响应式系统之嵌套怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue2响应式系统之嵌套怎么实现”文章吧...
    99+
    2023-06-30
  • Vue2 响应式系统之深度响应
    目录1、场景2、方案3、场景24、总结1、场景 import { observe } from "./reactive"; import Watcher from "./watche...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作