设计模式之观察者模式

基本概念

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其依赖者(观察者)会自动收到通知并更新。

在前端开发中,观察者模式常被用来实现组件间的数据传递和事件处理。比如,当一个组件的状态发生改变时,可以通过观察者模式来通知其他组件更新自身的状态或视图。

在观察者模式中,通常会定义两种角色:

1. Subject(主题):它是被观察的对象,当其状态发生改变时会通知所有的观察者。
1. Observer(观察者):它是观察主题的对象,当主题状态发生改变时会接收到通知并进行相应的处理。

观察者模式示例

// 定义主题对象
class Subject {
  constructor() {
    this.observers = [];
  }
  addObserver(observer) {
    this.observers.push(observer);
  }
  removeObserver(observer) {
    // this.observers = this.observers.filter(item => item !== observer);
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  }
  notify(data) {
    this.observers.forEach(observer => {
      observer.update(data);
    });
  }
}
// 定义观察者对象
class Observer {
  constructor(name) {
    this.name = name;
  }
  update(data) {
    console.log(`${this.name} received data: ${data}`);
    // 处理接收到的数据
  }
}
// 使用示例
const subject = new Subject();
const observer1 = new Observer('observer1');
const observer2 = new Observer('observer2');
subject.addObserver(observer1);
subject.addObserver(observer2);
// 通知所有观察者
subject.notify("Hello World"); 
// observer1 received data: Hello World
// observer2 received data: Hello World

在上述示例中,我们定义了一个主题对象 Subject和一个观察者对象 Observer。主题对象负责维护一组观察者对象,并在状态变化时通知观察者。观察者对象通过注册到主题对象中,接收到主题的通知后进行相应的处理。

Vue2 双向绑定

Vue2 源码通过观察者模式实现了双向数据绑定。下面是 Vue2 源码中实现双向绑定的大致代码实现:

  1. 首先,Vue 通过 Object.defineProperty 方法对数据对象进行劫持,将其转化为响应式对象。在这个过程中,Vue 会为每个属性创建一个 Dep 对象,用于收集依赖和通知更新。
  2. 在模板编译阶段,Vue 会解析模板中的指令和表达式,并创建对应的指令对象。每个指令对象都会关联一个 Watcher 对象。
  3. Watcher 对象负责订阅数据变化,并在数据变化时执行相应的回调函数。它会将自身添加到相关属性的依赖(Dep)中。
  4. 当数据发生变化时,被劫持的属性会触发相应的 setter 函数。在这个过程中,属性关联的依赖(Dep)会通知所有订阅者(即相关的Watcher)进行更新。
  5. 更新过程中,订阅者(即相关的Watcher)会执行回调函数,并更新视图。
  • 定义观察者对象
    // 定义观察者对象
    class Watcher {
      constructor(vm, key, updateFn) {
        this.vm = vm;
        this.key = key;
        this.updateFn = updateFn;
        Dep.target = this;
        this.vm[this.key]; // 触发 getter,收集依赖
        Dep.target = null;
      }
      // 更新视图
      update() {
        this.updateFn.call(this.vm, this.vm[this.key]); 
      }
    }
  • 定义依赖对象
    // 定义依赖对象
    class Dep {
      constructor() {
        this.subscribers = [];
      }
      // 收集依赖
      depend() {
        if (Dep.target && !this.subscribers.includes(Dep.target)) {
          this.subscribers.push(Dep.target); 
        }
      }
      // 通知更新
      notify() {
        this.subscribers.forEach((subscriber) => subscriber.update()); 
      }
    }
  • 定义响应式数据劫持函数
    // 定义响应式数据劫持函数
    function defineReactive(obj, key, val) {
      const dep = new Dep();
      Object.defineProperty(obj, key, {
        get() {
          // 收集依赖
          dep.depend(); 
          return val;
        },
        set(newVal) {
          if (newVal !== val) {
            val = newVal;
            // 通知更新
            dep.notify(); 
          }
        },
      });
    }
  • 创建 Vue 实例
    // 创建Vue实例
    class Vue {
      constructor(options) {
        this.data = options.data;
        for (let key in this.data) {
          defineReactive(this.data, key, this.data[key]);
        }
        new Watcher(this, options.key, options.updateFn);
      }
    }

以上是对 Vue2 源码实现双向绑定的简要描述。定义了 Watcher 观察者对象和 Dep 依赖对象。在defineReactive 函数中,我们使用 Object.defineProperty 对数据对象进行劫持,当数据发生变化时,会触发相应的 getter 和 setter 函数。在 getter 函数中,我们收集依赖;在 setter 函数中,我们通知依赖进行更新。通过创建 Vue 实例时创建的观察者对象,我们实现了数据和视图之间的双向绑定。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘子味的冰淇淋~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值