实现vue中effect的分支切换

49 阅读3分钟

前言

今天看的相较于昨天要少很多,还是在昨天的代码的基础上继续完善effect函数的功能。但是感觉整个脑海中的整个体系还是比较凌乱,没有很深入的理解代码的组织流程,还需要进一步学习和复盘...

总之先写下来好了,明天开始就是新的模块了,过两天忘记了还可以回来接着看🤪

今日完善的功能 & 一些理解

今天不放大片的代码了,大致谈一下自己的理解。

看视频的时候,老师叽里呱啦说一大堆,也没有太明白这个功能实现的需求到底是什么???倒是照着敲完了以后自己对着代码乱琢磨外加让AI给点灵感,大概理解了。 分支切换呢,实际上就是对应了一种开发中的常见情况,比如:

<template>
    <button @click="toggle"></button>
    <div v-if="data.a">{{data.b}}</div>
    <div v-else>{{data.c}}</div>
</template>
<script>
    const data = reactive({
        a:true,
        b:10,
        c:20
    })

    const toggle = ()=>{
        data.a=!data.a
    }
</script>

面对这样的情况,toggle函数在每次用户点击按钮时改变data.a的值,进行取反。但是不要忘记了,data可是响应式数据,改变它的值会触发reactive内部的set拦截器,让所有依赖重新执行渲染。这样会出现什么情况呢?

没错,就是即使在页面只展示一个标签时,另一个标签内的数据也会继续更新计算,所有依赖的收集只有进没有出😅

如果这样的情况少或者页面相对简单还好,一旦复杂起来,对性能的影响极大。所以这时我们需要切换effect关注的分支。具体实现起来呢,就是将之前收集的依赖清空,然后重新收集依赖,对新的依赖进行更新渲染。那么为了能清理依赖,需要定义一个清理函数cleanupEffect

function cleanupEffect(effect) {

    const { deps } = effect; //deps中装的是响应式数据对应的effect

    for (let i = 0; i < deps.length; i++) {

        deps[i].delete(effect); //解除effect重新收集

    }

    // 此处需要注意,上述繁琐的遍历是为了清除deps中存储的多个set中的依赖

    // 因为deps本身是一个数组,存储的是多个set的 **引用**

    // 所以需要遍历来清除各个set对应的内存上的数据

    // 不可以直接用: effect.deps=[] 来代替

    // 如果直接这样做,effect.deps这个数组确实空了,但是set里的内容依然在

    // 等于内存泄漏

    effect.deps.length = 0; //清空effect中的响应式数据

}

大概长这样,不过为了防止来回清除 / 写入导致混乱,我们不能把effects始终放在一块内存上,于是我们拷贝一份旧的数据到新的位置:

effects = new Set(effects);

// 此处将原有的effects拷贝了一份到新的内存地址

// 这样在清理并重新收集effects时就不会出现死循环

好了,大功告成!

不过呢,还有一个小小的功能,vue源码实现了,但是我们还没实现,就是停止effect对于依赖的跟踪,那么现在已经有run方法和cleanupEffect了,这个就很好办了,再写一个stop方法就好了。

stop() {

    if (this.active) {

        this.active = false;

        cleanupEffect(this);

        // 停止effect的收集

    }

}

代码中的active作为一个开关属性,控制effect开闭,然后每次关闭后再清理干净effect中存储的依赖就可以了,下次开启effect再通过调用run方法打开。

那就到这吧,睡觉去了😪