vue3—数据响应

801 阅读3分钟

vue3—数据响应

定义数字 a 与数据 b、且 b = a + 10,假设数据 a 每次发生改变时,我们都想实时打印 b 的值

基础

let a, b;

a = 10;
b = a + 10;
console.log(b); // 20

a = 20;
b = a + 10;
console.log(b); // 30

非常明显的,每次我们改变 a 的数据时,我们都需要手动调用去更新 b 的值,最后才会得到我们想要的值

基础的优化

let a, b;

a = 10;
changeUpdate();

a = 20;
changeUpdate();

function changeUpdate() {
  b = a + 10;
  console.log(b);
}

优化版实际上只是将 b 更新的规则封装,但本质上每次 a 的值发生变化,还是需要我们手动去更新。

那有没有一种方案是: 我只要改变 a 的值 、设置需要更新对象 b 的规则,它自动触发呢

vue3版

安装

npm i @vue/reactivity

ref

// 引入
const { effect, ref } = require("@vue/reactivity");

let a, b;

// 定义响应式的数据  a
a = ref(10);

// 设置更新规则
function changeUpdate() {
  b = a.value + 10;
  console.log(b);
}
effect(changeUpdate);

// 再次更新 a 的值
a.value = 20;

reactive

const { effect, reactive } = require("@vue/reactivity");

// 定义响应式的数据  a
a = reactive({
  value: 10,
});

上面的例子 仅需对声明的数据 a 进行处理,设定更新规则,然后 a 的值更新之后就会执行

简单实现响应式

ref

// 全局存储当前需要收集的依赖
let currentEffect;

// 收集依赖
class Dep {
  constructor(val) {
    // 利用 Set 防止依赖重复收集
    this.effects = new Set();
    // 创建类时定义值、 利用 get set 就能监听到其的获取和更新
    this._val = val;
  }
  depend() {
    // 收集当前存储的依赖
    if (currentEffect) {
      this.effects.add(currentEffect);
    }
  }
  notice() {
    // 发布当前的依赖
    this.effects.forEach((effect) => {
      effect();
    });
  }
  get value() {
    // 定义值时 订阅
    this.depend();
    return this._val;
  }
  set value(newVal) {
    this._val = newVal;
    // 值更新完毕之后 发布
    this.notice();
  }
}

function effect(fn) {
  // 收集当前依赖, 收集之后清空
  currentEffect = fn;
  // 定义之后先执行一次
  fn();
  currentEffect = null;
}

// 声明响应式数据   类似与 ref
let a = new Dep(10);

// 定义更新规则
effect(() => {
  let b = a.value + 10;
  console.log(b);
});

// 再次更新
a.value = 20;

reactive

// 全局存储当前需要收集的依赖
let currentEffect;

// 收集依赖
class Dep {
  constructor(val) {
    // 利用 Set 防止依赖重复收集
    this.effects = new Set();
    // 创建类时定义值、 利用 get set 就能监听到其的获取和更新
    this._val = val;
  }
  depend() {
    // 收集当前存储的依赖
    if (currentEffect) {
      this.effects.add(currentEffect);
    }
  }
  notice() {
    // 发布当前的依赖
    this.effects.forEach((effect) => {
      effect();
    });
  }
  get value() {
    // 定义值时 订阅
    this.depend();
    return this._val;
  }
  set value(newVal) {
    this._val = newVal;
    // 值更新完毕之后 发布
    this.notice();
  }
}

function effect(fn) {
  // 收集当前依赖, 收集之后清空
  currentEffect = fn;
  // 定义之后先执行一次
  fn();
  currentEffect = null;
}

let targetMap = new Map();

// get set 相同的代码封装
function getDep(target, key) {
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }

  let dep = depsMap.get(key);
  if (!dep) {
    dep = new Dep();
    depsMap.set(key, dep);
  }
  return dep;
}

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      // 每一个 target 都对应一个 Map、而对应的 Map 中 key 存储的是其对应的 依赖收集类 dep
      // 最后的目的是 为了获取到每个 key 的 dep 实例对象,让其收集依赖
      let dep = getDep(target, key);
      // 收集依赖
      dep.depend();
       // Reflect 和 Object 方法的一些区别
      // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/Comparing_Reflect_and_Object_methods
      return Reflect.get(target, key);
    },
    set(target, key, val) {
      let dep = getDep(target, key);
      let result = Reflect.set(target, key, val);
      // 值改变时 发布依赖
      dep.notice();
      return result;
    },
  });
}

// 声明响应式数据
let a = new reactive({
  value: 10,
});

// 定义更新规则
effect(() => {
  let b = a.value + 10;
  console.log(b);
});

// 再次更新
a.value = 20;