JS:手撸观察者模式和发布订阅模式

164 阅读1分钟

1、观察者模式

当对象间存在一对多的关系,使用观察者模式。比如:当一个对象被修改,会自动通知依赖它的对象。

let uid = 0
class Dep {
  constructor() {
    this.id = uid++
    // 存储所有的 watcher
    this.subs = []
  }
  addSub(sub) {
    this.subs.push(sub)
  }
  removeSub(sub) {
    if(this.subs.length) {
      const index = this.subs.indexOf(sub)
      if(index > -1return this.subs.splice(index, 1)
    }
  }
  notify() {
    this.subs.forEach(sub => {
      sub.update()
    })
  }
}

class Watcher {
  constructor(name) {
    this.name = name
  }
  update() {
    console.log('更新')
  }
}

2、发布订阅模式

与观察者模式相似,区别在于发布者和订阅者是解耦的,由中间的调度中心去与发布者和订阅者通信。
Vue响应式原理个人更倾向于发布订阅模式。其中 Observer 是发布者,Watcher 是订阅者,Dep 是调度中心。

class EventEmitter {
  constructor() {
    this.events = {}
  }
  on(type, cb) {
    if(!this.events[type]) this.events[type] = []
    this.events[type].push(cb)
  }
  emit(type, ...args) {
    if(this.events[type]) {
      this.events[type].forEach(cb => {
        cb(...args)
      })
    }
  }
  off(type, cb) {
    if(this.events[type]) {
      const index = this.events[type].indexOf(cb)
      if(index > -1) this.events[type].splice(index, 1)
    }
  }
}

3、Vue.observable 的了解

Vue.observable 可使对象可响应。返回的对象可直接用于渲染函数计算属性内,并且在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器。
Vue 2.x 中传入的对象和返回的对象是同一个对象。
Vue 3.x 则不是一个对象,源对象不具备响应式功能。

适用的场景:在项目中没有大量的非父子组件通信时,可以使用 Vue.observable 去替代 eventBusvuex方案。

// store.js
import Vue from 'vue'
export const state = Vue.observable({
  count1
})
export const mutations = {
  setCount(count) {
    state.count = count
  }
} 

// vue 文件
<template>
  <div>{{ count }}</div>
</template>
<script>
import { state, mutation } from './store.js'
export default {
  computed: {
    count() {
      return state.count
    }
  }
}
</script>