vue3源码学习——computed

文章目录

  • vue3源码学习——computed
    • 基础
    • 修改effect 函数
    • 解决问题
  • 代码执行过程图

computed是在上篇文章vue3源码学习——响应式reactive的基础上实现的。看完后再来看这篇吧。

vue3源码学习——computed

基础

基本html代码

    <h1>打开控制台,进行调试h1>
    <h2>h2>
    <button onclick="change()">Click mebutton>

    <script src="./reactive.js">script>
    <script src="./effect.js">script>
    <script src="./computed.js">script>
    <script>

         // 1. 响应式数据
        const data = reactive({
       count: 1 });
        // 2. 计算属性
        const double = computed(() => {
      
            return data.count * 2
        });
        // 3. 依赖收集
        effect(() => {
      
            console.warn("effect");
            document.getElementsByTagName('h2')[0].innerText = double.value//double.value会触发
        });
        // 4. 触发上面的effect重新执行
        const change = () => {
      
            data.count++;
        };
    script>

computed其实也就相当于在执行一个函数,并进行监听,前面已经写了effect,现在只需要两者进行关联即可。

//computed.js

function computed(fn){
     
    //lazy 不会立即执行
    const runner = effect(fn,{
     
        lazy:true
    })
    return {
     
        get value(){
     
            return runner()
        },
    }
}

先进行关联,关联时不会立即执行。在get时,执行。

所以关联时,要返回一个已经关联的方法。

//effect.js
//原来的
function effect(fn) {
     
    try {
     
        effectFn = fn;
        fn();
    } finally {
     
        effectFn = null;
    }
}

修改effect 函数

//添加options 参数
//lazy为true 时,不执行。

function effect(fn,options) {
     
    
  let effect = createReactiveEffect(fn)//①要返回一个方法②将fn进行缓存
  if (!options.lazy) {
     
    effect();
  }
   return effect
}

let effect = createReactiveEffect(fn)//①要返回一个方法②将fn进行缓存

function createReactiveEffect(fn) {
     
    const bbeffect = function(){
     
        try{
     
            effectFn = bbeffect;
            return fn();
        }finally{
     
            effectFn = null
        }
    }
    return bbeffect
}

bbEffect是一个闭包函数,闭包函数的作用
一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
这里是将bbeffect,fn始终保存在内存中

将fn缓存并运行,运行完后,将其清除。

唯一的差别就是运行函数的结果要返回。否则没有返回值就是undefined。

执行过程。

vue3源码学习——computed_第1张图片

①当执行到

const  = computed(() => {
      
    console.warn('computed')
    return data.count * 2;
});

runner = bbEffect闭包函数(bbEffect、fn存放在内存中)

等待get时,运行。

为了方便描述,这里的fn命名为fn1,下面的为fn2

②当执行到

effect(() => {
      
    console.warn("effect");
    document.getElementsByTagName('h2')[0].innerText = double.value//double.value会触发
});
  • 存储成闭包函数,并执行。
  • effectFn缓存当前闭包(fn2),并执行fn2。
    • double.value 触发computed 的get函数,执行runner。
    • effectFn缓存runner(fn1)闭包,并执行fn1。 ⚠️此时effectFn(fn2)被覆盖了
    • data.count 触发reactive 的get(执行track,将effectFn(此时是fn1的闭包)保存到targetMap中与count进行关联。)
    • effectFn=null
  • effectFn=null ⚠️ 即使effectFn(fn2)没有被覆盖,他也没有进行进行关联(track)

解决问题

  • 用栈和变量解决覆盖问题。

    effectFn ——> effectStack

    activeEffect——当前依赖)

    //收集依赖
    ...
    //get 时 收集依赖
    function track(target, type, key) {
           
       
        if (activeEffect) {
           
            ...
            // 放入依赖 effect就是依赖
            if (!dep.has(activeEffect)) {
           
                console.log('无effect,并放入effect')
                dep.add(activeEffect)
            }
        }
    }
    //set 时更新
    ...
    //临时存放 effect中的fn参数
    let effectStack = [];
    let activeEffect;
    //响应式副作用
    ...
    function createReactiveEffect(fn) {
           
        const bbeffect = function(){
           
            try {
           
                effectStack.push(bbeffect);
                activeEffect = bbeffect
                return fn();
            } finally {
           
                effectStack.pop();
                activeEffect = effectStack[effectStack.length - 1]
            }
        }
        return bbeffect
    }
    
    
  • 使用双向绑定依赖

    function createReactiveEffect(fn) {
           
        const bbeffect = function(){
           
            ...
        }
        bbeffect.deps=[];//存放依赖
        return bbeffect
    }
    
    function track(target, type, key) {
           
       
        if (activeEffect) {
           
            ...
            // 放入依赖 effect就是依赖
            if (!dep.has(activeEffect)) {
           
                console.log('无effect,并放入effect')
                dep.add(activeEffect)
                activeEffect.deps.push(dep) //放入依赖,形成双向依赖
            }
        }
    }
    
    

    当执行完computed的get函数时,执行完runner后,activeEffect对应fn2,应将其存入依赖。

    const computed={
           
      get value() {
           
        let value = runner();
        //手动添加依赖
        for (let i = 0; i < runner.deps.length; i++) {
           
          const dep = runner.deps[i];
          dep.add(activeEffect);
        }
    
        return value;
      },
    }
    

    此时依赖存放完后的target是

vue3源码学习——computed_第2张图片

代码执行过程图

effect的执行过程
vue3源码学习——computed_第3张图片

你可能感兴趣的:(vue3,vue3,vue3,源码分析,vue3,computed)