简版vue响应式实现

20 篇文章 0 订阅
8 篇文章 0 订阅

前言

此为自己学习vue源码响应式部分的一个小demo,按照自己的理解实现改变变量a,变量b也跟着改变的效果,整体实现较为简单,不考虑任何复杂场景,如有问题请大家指正

效果

data.age改变后,comouted.computedB的值也改变了

node index.js
// hello, computedb is 22
// hello, computedb is 59

代码

可以直接复制到浏览器控制台运行

// 发布者
class Observer {
  constructor() {
    this.listeners = [];
  }
  collect(t) {
    if (t) {
      this.listeners.push(t);
    }
  }
  publish() {
    this.listeners.forEach((item) => {
      item();
    });
  }
}

// 订阅者
class Watcher {
  constructor(v) {
    this.val = v;
  }
}
// 初始化方法,把数据变成一个 发布者
function becomeObserver(obj) {
  Object.keys(obj).forEach((item) => {
    const ob = new Observer();
    let val = obj[item];
    Object.defineProperty(obj, item, {
      get() {
        // 收集订阅者
        ob.collect(
          currentWatcher && currentWatcher.val ? currentWatcher.val : ""
        );
        return val;
      },
      set(newV) {
        val = newV;
        // 通知订阅者
        ob.publish();
      },
    });
  });
}
// 订阅方法, 把comouted 、watcher 等变为一个订阅者
function becomeWatcher(obj) {
  Object.keys(obj).forEach((item) => {
    const val = obj[item];
    Object.defineProperty(obj, item, {
      get() {
        // 把自己这个watcher放到对应的发布者的listeners里
        let pos = watcherList.findIndex((item) => item.val === val);
        if (pos !== -1) {
          currentWatcher = watcherList[pos];
        } else {
          watcherList.push(new Watcher(val));
          currentWatcher = watcherList[watcherList.length - 1];
        }
        return val;
      },
    });
  });
}
// 记录所有的订阅者
let watcherList = [];
// 记录当前正在使用的订阅者
let currentWatcher = null;
// data属性 ,对应vue组件中的data
var data = {
  age: 2,
};
// 对应vue组件里的comouted属性
var computed = {
  computedB() {
    const val = data.age + 20;
    console.log("hello, computedb is", val);
    return val;
  },
};

// 初始化data
becomeObserver(data);
// 初始化computed
becomeWatcher(computed);

// computed 调用, 模仿在vue 中使用computed属性
computed.computedB();

data.age = 39;


说明

我的理解:vue 的响应式就是一个发布订阅模式,发布者是 data,订阅者是 computed、watch、组件…,每次 data 改变,相应的 computed、watch 都会执行,页面也会刷新(组件执行)。

关键点1: 发布订阅双方是谁?
  1. 发布者:data
  2. 订阅者:computed、watch、组件
关键点2:发布者需要做两个操作,收集订阅者、通知订阅者,在哪里做这两个操作?

根据目前我们对 vue 的了解,都知道是在 get 的时候收集订阅者,在 set 的时候通知订阅者

关键点3:每个 comouted、watch 的属性都是一个订阅者,那个属性 a 在 get 的时候该收集哪一个呢?

哪个订阅者调用了 a,那么 a 就该收集该订阅者。

关键点4:属性 a 在 get 的时候,怎么知道是订阅者 b 调用了自己

订阅者 b 在调用属性 a 的时候(也就是在执行订阅者 b 的 get 的时候)需要把自己保存在全局变量 currentWatcher 中,那么属性 a 可以通过 currentWatcher 拿到订阅者 b 了

关键点5: 订阅者 b 是什么时候放到全局的 currentWacther 里的

从上面也可以看出是订阅者 b 在被使用的时候(get 的时候)记录到 currentWacther 的

关键点6:代码里的 watcherList 是干嘛的

订阅者 b 不能 get 一次就生成一个新的 Watcher,最终导致重复执行 comouted 的某个属性。所要用 watcherList 来记录已经被变成订阅者的 comouted、watche 等的属性。

End

文章地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值