「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」
要想复刻一份minivue,得先看看它本身的效果。 核心是reactivity这个包,之前已经下载过vue的全部文件,打了包global.js 。我就直接用这个了,不另外安装vue-next/reativity了。 然后把我们自己实现的代码放在core文件夹里。
先写一个demo看看原版的效果大概是什么样的, 先来个简单的ref effect 。
使用ref 创建一个响应式对象,然后再来一个effect监听。 结果是执行了两次回调。第一次就是创建监听立马就执行,第二次就是依赖发生变化触发了回调。
依赖收集的时机
原来,是在第一次立即执行回调时,收集了依赖。往后,只要依赖的值变化了,就再次执行回调。
也就是说,依赖收集就是发生在第一次执行回调的时候,后面再次执行的时候不会去收集。
手动实现依赖收集
依赖收集就是,就是把用到的响应式对象都给它来一个记录,记录这个回调,收集这个回调。
我们先简单伪实现一个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的第一步,后面继续。