Vue2响应式核心

282 阅读1分钟

vue3都来了。才开始写vue2...

vu2的响应式核心在与对object的属性的getter setter拦截,使用Object.defineProperty

Object.defineProperty(obj, key, {
      get() {
        console.log("getter")
        return obj[key];
      },
      set(newValue) {
        console.log("setter")
        obj[key] = newValue;
      }
    });

这样就可以在get的时候收集依赖,set的时候通知依赖

先写一个依赖收集的类

class Dep{
    constructe(){
        this.deps = new Set()
    }
    depend(update){
        this.deps.add(update)
    }
    notfiy(){
        this.deps.forEach(update=>update())
    }
}

这样就可以的把依赖存起来,但是有个问题,update是对哪个属性值依赖呢?

这就得先执行下依赖函数,看下他get了哪个key就是哪个key的update

let dep = new Dep() 
Object.defineProperty(obj, key, {
      get() {
        console.log("getter")
        dep.depend(()=>{
            console.log("依赖")
        })
        return obj[key];
      },
      set(newValue) {
        console.log("setter")
        obj[key] = newValue;
        dep.notfiy()
      }
    });

但是不能把依赖写在getter里,所以要把依赖单个存起来

let dep = new Dep() 
let update =()=>{console.log("依赖")}
Object.defineProperty(obj, key, {
      get() {
        console.log("getter")
        dep.depend(update)
        return obj[key];
      },
...

但这样还是要吧update传进去,所以还需要一个能从depend里直接去收集,而且依赖有很多个,但是依赖都是一个一个调用的,所以只需要一个全局变量保存当前调用的依赖就可以了

let update = ()=>{console.log("依赖")}
//update是可变的
let active = update
...
    depend(){
        this.deps.add(active)
    }
...
let dep = new Dep() 
Object.defineProperty(obj, key, {
      get() {
        console.log("getter")
        dep.depend()
        return obj[key];
      },
...

那么,什么时候active变呢?这是就依赖需要一个主动调用的过程

function autorun(update) {
  const wrappedUpdate = () => {
    active = wrappedUpdate;
    update();
    active = null;
  };
  wrappedUpdate();
}

那么简单的原理就差不多了。

//拦截getter setter
function obsever(obj) {
  if (typeof obj != "object" || obj === null) {
    return;
  }
  Object.keys(obj).forEach((key) => {
    let value = obj[key];
    let dep = new Dep();
    Object.defineProperty(obj, key, {
      get() {
        dep.depend();
        return value;
      },
      set(newValue) {
        const changed = newValue !== value;
        value = newValue;
        changed && dep.notify();
      }
    });
    //递归拦截
    obsever(value);
  });
}
//依赖订阅通知
class Dep {
  constructor() {
    this.deps = new Set();
  }
  depend() {
    activeUpdate && this.deps.add(activeUpdate);
  }
  notify() {
    this.deps.forEach((fn) => fn());
  }
}

//autorun 建立依赖
function autorun(update) {
  const wrappedUpdate = () => {
    activeUpdate = wrappedUpdate;
    update();
    activeUpdate = null;
  };
  wrappedUpdate();
}

let activeUpdate;
let state = {
  a: {
    b: 2
  },
  c: 1
};
//响应式
obsever(state);
//建立依赖
autorun(() => {
  console.log(state.a.b);
});
state.c++;