【学习】发布订阅者模式在小程序的运用
发布于 5 年前 作者 zhoutao 3930 次浏览 来自 分享

【设计模式】 发布订阅者模式在小程序的运用

最近在学习设计模式,设计模式无关语言,也无关产品,是一种编码的思维方式,可以使我们更好的管理我们的代码,最大的好处莫过于 解耦,从业务代码中抽离出可复用高的代码,不同模块相互独立;

这篇文章开始记录一下 发布——订阅模式

介绍

Javascript设计模式与开发实践 一书对其解释如下:

发布—订阅模式又叫观察者模式,它定义对象间 的一种一对多的依赖关系,当一个对象的状态发 生改变时,所有依赖于它的对象都将得到通知。 在JavaScript开发中,我们一般用事件模型来替代 传统的发布—订阅模式

书中还有一个关于网站登录的例子的解释很精彩,生动的解释该模式下的模块分离以及该模式下的具体运用。

小程序端的运用

由于最近接触的小程序端的开发较多,因此,打算用一个小程序的场景来学习实践一下 发布——订阅模式

场景

A页面是一个列表页面,点击列表某项进入详情页面,在详情页面执行删除(修改)操作,后退返回A页面,需要刷新接口,从新获取数据;

实战

  • 编写发布-订阅模式对象
// observer.js
const Event = (() => {
  // 通知模块列表
  let clientList = []

  /**
   * 监听
   * @param {*} key 模块名称
   * @param {*} fn 回调函数
   */
  const listen = function(key, fn) {
    // 判断是否存在为key值的模块,没有需要初始化空数组
    if (!clientList[key]) {
      clientList[key] = []
    }
    // 模块添加监听回调函数
    clientList[key].push(fn)
  }

  /**
   * 发布通知
   */
  const trigger = function() {
    const key = Array.prototype.shift.call(arguments)
    const fns = clientList[key]

    // 如果该模块下没有注册事件,直接返回
    if (!fns || fns.length === 0) {
      return false
    }

    // 执行模块注册的所有事件
    fns.map(fn => fn && fn.apply(this, arguments))
  }

  /**
   * 注销事件
   * @param {*} key 
   * @param {*} fn 
   */
  const remove = function(key, fn) {
    const fns = clientList[key]

    // 模块没有注册事件
    if (!fns) {
      return false
    }

    // 没有传入需要注销的事件,全部清空
    if (!fn) {
      fns && (fns.length = 0) // 这里很精彩,直接数组清空即可
    } else {
      // 清掉该事件
      const index = fns.findIndex(item => item === fn)
      if (index !== -1) {
        fns.splice(index, 1)
      }
    }
  }

  return {
    listen,
    trigger,
    remove,
  }
})();

module.exports = Event
  • 编写列表页面,注册列表删除事件
// observerPage.js
const Event = require('../../designPatterns/observer');

Page({
  data: {
    list: [
      {id: 1, value: '第1项'},
      {id: 2, value: '第2项'},
      {id: 3, value: '第3项'},
      {id: 4, value: '第4项'},
      {id: 5, value: '第5项'},
      {id: 6, value: '第6项'},
      {id: 7, value: '第7项'},
      {id: 8, value: '第8项'},
      {id: 9, value: '第9项'},
      {id: 10, value: '第10项'},
    ]
  },

  onLoad() {
    // 注册通知
    Event.listen('observerList', this.delete)
  },

  delete(id) {
    const newList = this.data.list.filter(item => item.id !== +id)
    this.setData({ list: newList })
  },

  toOperatePage(e) {
    const { item } = e.currentTarget.dataset
    wx.navigateTo({
      url: `./operate?id=${item.id}`
    })
  }
})
  • 编写操作页面,发布删除操作
const Event = require('../../designPatterns/observer');

Page({
  data: {
    id: 1,
  },

  onLoad(options) {
    this.setData({id: options.id})
  },

  handleDel() {
    wx.showToast({
      title: '删除成功'
    })
    Event.trigger('observerList', this.data.id)
  }
})

小结

创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中(也就是clientList对象中);过度使用必然会造成堆栈溢出,而且模块之间的关联会隐藏其中,难以排查bug。

项目调试地址

项目调试地址: https://github.com/csonchen/mina-app

感谢

  • Javascript设计模式与开发实践 —— 曾探
回到顶部