复制一份minivue(一)

139 阅读2分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

要想复刻一份minivue,得先看看它本身的效果。 核心是reactivity这个包,之前已经下载过vue的全部文件,打了包global.js 。我就直接用这个了,不另外安装vue-next/reativity了。 然后把我们自己实现的代码放在core文件夹里。image.png 先写一个demo看看原版的效果大概是什么样的, 先来个简单的ref effect 。 使用ref 创建一个响应式对象,然后再来一个effect监听。 结果是执行了两次回调。第一次就是创建监听立马就执行,第二次就是依赖发生变化触发了回调。

image.png

依赖收集的时机

原来,是在第一次立即执行回调时,收集了依赖。往后,只要依赖的值变化了,就再次执行回调。

也就是说,依赖收集就是发生在第一次执行回调的时候,后面再次执行的时候不会去收集。

手动实现依赖收集

依赖收集就是,就是把用到的响应式对象都给它来一个记录,记录这个回调,收集这个回调。

我们先简单伪实现一个ref,然后收集依赖用set存储。 收集依赖这个动作是响应式对象本身的, 它察觉到,有个函数在使用它,于是乎就把这个函数在小本本上记下来了。 我们用一个外部变量来临时中转回调,第一次执行完毕就清除,这样就做到了,只在创建watch的时候收集依赖。

let currentEffect = null ;
class Dep{
    constructor(value){
        this._val = value;
        this.effects = new Set() // effect 不重复
    }
    /* 收集依赖 */
    depend(){
        currentEffect && this.effects.add(currentEffect)
    }
    get value (){ return this._val}
}

function watchEffect(fn){
currentEffect = fn ;
fn()
currentEffect = null
}

这样怎么收集依赖呢,手动收集,用到哪个响应式对象就给它收集依赖。我们先把自己实现的dep 和 watch引入。就是在fn里直接调用依赖的收集方法。

import {watchEffect, Dep} from './core/index.js'
    const num1 = new Dep(20) ;
    watchEffect(()=> {
        TenCount = num1.value+ 10 ;
        num1.depend() ;
        console.log('手动收集依赖') ;
    })
    num1.value *=10 ;

手动触发依赖

同样的逻辑,我们手动触发,只要遍历之前收集的依赖挨个执行就行了。

class Dep{
    notice(){
        this.effects.forEach((effect)=> {
            effect()
        })
    }
  }

执行 num1.value *=10 ; num1.notice()这样我们的手动实现依赖收集和触发就完成了。

自动执行依赖收集和触发

用到了某个依赖,必然会去读取它,所以在getter里面收集依赖即可。 依赖变化必然是修改了它,因此在setter里去触发依赖。就是把我们之前的方法写在setter和getter里。

就是要注意一点,setter的回调必须写在赋值 之后,这样才能拿到最新值。

    get value (){    this.depend()  ; return this._val}
    set value (val){  this._val = val ;    this.notice()       }

这样就实现了minivue的第一步,后面继续。