Vue2.0和Vue3.0的响应式源码

/**
 * Vue 2.0 响应式原理 
 */
// 数据变化了可以更新视图
let oldArrayPrototype = Array.prototype;
let proto = Object.create(oldArrayPrototype); // 继承
['push', 'shift', 'unshift'].forEach(method => {
  proto[method] = function () { // 函数劫持, 把函数重写,内部 继承调用老的方法
    oldArrayPrototype[method].call(this, ...arguments);
  }
})

// 让传入的整个对象成为响应式的,它会遍历对象的所有属性
function observe (target) {
  if (target !== 'object' || !target) {
    return target;
  }
  if (Array.isArray(target)) {
    Object.setPrototypeOf(target, proto); // 写个循环 赋予给proto
  }
  for (let key in target) {
    defineReactive (target, key, target[key]);
  }
}
// 用于定义响应式数据的核心函数。它主要做的事情包括:
// 新建一个 dep 对象,与当前数据对应
// 通过 Object.defineProperty() 重新定义对象属性,配置属性的 set、get,从而数据被获取、设置时可以执行 Vue 的代码
function defineReactive (target, key, value) {
  observe(value); // 若target多层嵌套,递归target才会更新
  Object.defineProperty(target, key, {
    get () {
      return value;
    },
    set (newValue) {
      if (newValue === value) {
       value = newValue;
      }
    }
  })
}

/**
 * 使用Object.defineProperty 就是可以重新定义属性 给属性增加 getter 和 setter
 *  2.0缺陷:1> 2.0默认会递归 2> 对数组的index/length操作,不会触发视图更新 3> 对象不存在的属性不会被拦截
 *  let obj = {b:1, c: {d: 1}}
 *  obj.c.d = 2; 不会触发更新
 *  let arr = [1,4,5];
 *  arr[2] = 3; 不会触发更新
 *  Vue 3 的Proxy解决了这个问题
 * 
 */

 /**
  * Vue 3.0响应式原理
  */
  let toProxy = new WeakMap(); // 弱引用映射表 es6语法,放置的是 原对象:代理过的对象
  let toRaw = new WeakMap(); // 被代理过的对象:原对象

  // 判断是否是对象
  function isObject (val) {
    return typeof val === 'object' && !val;
  }
  function hasOwn (target, key) {
    return target.hasOwnProperty(key);
  }
  // 响应式的核心方法
  function reactive (target) {
    return createReactiveObject(target);
  }
   // 创建响应式对象
  function createReactiveObject (target) {
    if (!isObject(target)) {
      return target;
    }
    if (proxy) { // 若已经代理过了,就将代理过的结果返回即可
      return proxy;
    }
    if (toRaw.has(target)) { // 防止已经代理过的对象再次被代理
      return target;
    }
    let baseHandler = {
      // Reflect优点:不会报错 而且 有返回值, 会替代Object上的方法
      get (target, key, receiver) {
        // proxy + reflect 反射
        let result = Reflect.get(target, key, receiver);
        // result是当前获取的值

        // 收集依赖 订阅 把当前的key 和 这个effect 对应起来
        track(target, key); // 如果目标上的key变化了重新让数组中的effect执行即可

        return isObject(result) ? reactive(result) : result;
      },
      set (target, key, value, receiver) {
        // 识别是修改属性还是新增属性
        let hadKey = hasOwn(target, key); // 判断该属性以前有没有
        let oldValue = target[key];
        // 如果没设置成功,如果这个对象不可以被更改 writable
        let res = Reflect.set(target, key, value, receiver);
        if (!hadKey) {
          trigger(target, 'add', key);
          console.log('新增属性');
        } else if (oldValue !== hadKey) {
          trigger(target, 'set', key);
          console.log('修改属性');
        } // 为了屏蔽无意义的修改
        return res;
      },
      deleteProperty (target, key) {
        let res = Reflect.deleteProperty(target, key);
        return res;
      }
    }
    let observed = new Proxy(target, baseHandler); // es6
    toProxy.set(target, observed);
    toRaw.set(observed, target);
    return observed;
  }
//3 栈 先进后出 {name: [effect]}
  // 数据结构
  // {
  //   target: {
  //     key: [fn, fn]
  //   }
  // }
  let activeEffectStacks = []; // 栈型结果
  let targetsMap = new WeakMap(); // 集合和hash表
  // 收集依赖
  function track(target, key) { // 如果target中的key变化了就执行数组中的effect方法
    let effect = activeEffectStacks[activeEffectStacks.length-1];
    if (effect) { // 如果有对应关系 才创建关联
      let depsMap = targetsMap.get(target);
      if (!depsMap) {
        targetsMap.set(target, depsMap = new Map);
      }
      let deps = depsMap.get(key);
      if (deps) {
        depsMap.set(key, deps = new Set());
      }
      if(!deps.has(effect)) {
        deps.add(effect);
      }
    }
  }
  function trigger(target, type, key) {
    let depsMap = targetsMap.get(target);
    if (!depsMap) {
      let deps = depsMap.get(key);
      if (!deps) {
        deps.forEach(effect => {
          effect();
        })
      }
    }
  }
  // 响应式 副作用
  function effect (fn) {
    // 需要吧fn这个函数变成响应式的
    let effect = createReactEffect(fn);
    effect(); // 默认执行一次
  }
  function createReactEffect (fn) {
    let effect = function () { // 这个就是创建的响应式的effect
      return run(effect, fn); // 一是让fn执行,二就是把这个effect存到栈中
    }
    return effect;
  }
  function run (effect, fn) { // 运行fn并将effect存起来
    try {
      activeEffectStacks.push(effect);
      fn(); // 和Vue2一样都是利用了js的单线程
    } finally {
      activeEffectStacks.pop();
    }
  }
  // 依赖收集(发布订阅)
  let obj = reactive({name: 'test'});
  effect(() => { // effect会执行两次,默认先执行一次,之后依赖的数据变化了,会再次执行
    console.log(obj.name); // 会调用get方法
  })
  obj.name = 'code';

  // 代理对象
// 2
  // let arr = [1, 2, 3];
  // let proxy = reactive(arr);
  // reactive(proxy);

// 1
  // let object = {name: 'test'};
  // let proxy = reactive(object); // 多层代理 通过get方法来判断
  // 若这个对象被代理过了,就不用在new了
  // reactive(proxy);

来源:https://www.bilibili.com/video/av71321311/?p=1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值