js设计模式之发布-订阅模式

一. 发布-订阅

发布订阅模式又叫观察者模式,在某些文章中会阐述发布-订阅模式和观察者模式之间的区别,本文不阐述两者之间的具体差异,只阐述发布订阅模式的通用实现,以及如何借助该模式实现数据的变动到页面的更新。

二. 基本实现

	/**
    *  发布订阅(又叫观察者模式)
    *  实现发布订阅模式
    **/

    class Event {
        constructor() {
            // 缓存保存回调函数
            this.callbacks = {}
        }
        /**
         * 发布事件
         * @param {*} name
         * @param {*} args
         * 
         */
        $emit(name, args) {
            const cbs = this.callbacks[name];
            if (!cbs || cbs.length === 0) return false;
            cbs.forEach(cb => cb.call(this, args))
        }

        /**
         * 
         * 订阅事件
         * @param {*} name
         * @param {*} fn
         * 
         */

        $on(name, fn) {
            if (!this.callbacks[name]) {
                this.callbacks[name] = []
            }
            if(typeof fn !== 'function') {
				throw new Error('param:fn must be a function')
			}
            this.callbacks[name].push(fn)
        }

        /**
        * 
        * 取消订阅事件
        * @param {*} name
        * @param {*} fn
        * 
        */
        $off(name) {
            this.callbacks[name] = null
        }
    }

三. 数据驱动视图更新

// html
<ul>
   <li>苹果价格:<span class="apple">1</span></li>
   <li>香蕉价格:<span class="banana">2</span></li>
</ul>

// watch方法
function watch(target) {
        if (typeof target !== 'object') {
            throw new Error('param must be a targetect');
        }
        let newTarget = {}
        for (let prop in target) {  //这里如果不使用let需要使用闭包
            // 监听数据变化
            event.$on(prop, function (val) {
                // 更新视图
                document.querySelector('.' + prop).innerText = val;
            })
            // 初始化页面数据
            event.$emit(prop, target[prop])
            if (target.hasOwnProperty(prop)) {
                Object.defineProperty(newTarget, prop, {
                    get: function () {
                       return target[prop]
                    },
                    set: function (val) {
                        console.log(val);
                        if (val === newTarget[prop]) {
                            return console.log('数据未发生变化');
                        }
                        target[prop] = val
                        event.$emit(prop, val)
                    }
                })
            }
        }
        return newTarget
 }
 // 设置价格
 let fruits = {
        apple: 2,
        banana: 3
 }
let f2 = watch(fruits)
// 更新价格
setTimeout(() => f2.apple = 16, 2000)

四. 发布订阅模式应用

发布订阅模式主要用于js异步操作的场景

  • vuemvvm框架的响应式实现

  • js事件模型 window.addEventListener(eventType, fn, false)

  • 网站登录(本实例来源于javascript设计模式与开发实践8.7)

    商城网站里有header头部,nav导航,消息列表购物车等模块,这几个模块的渲染有一个共同的前提条件,就是必须先用ajax异步请求获取用户的登录信息,比如用户的名字和头像要显示在header模块里,而这两个字段都来自用户登录后返回的信息。

    • 传统ajax 实现

      login.success(data => {
      	header.setAvatar(data.avatar) 		//设置header模块的头像
      	nav.setAvatar(data.avatar) 			//设置nav模块的头像
      	message.refresh() 					//刷新消息列表
      	cart.refresh()                     // 刷新购物车列表
      	
      })
      
      

    现在编写登录模块,还必须了解其他模块的变量名和方法,并且变量保持一致,这是针对具体编程实现的例子,当header模块的设置头像方法改名后,login模块也要做相应修改,这不利于代码的维护和新增功能(比如刷新收货地址)。

    • 发布-订阅模式实现

      	// login模块
      	//发布登录成功的消息
      	$.ajax(url, data => login.trigger('loginSucc', data)) 
      	
      	// 其他模块监听
      	let header = (function() {
      		login.listen('loginSucc', data => {
      				header.setAvatar(data.avatar)
      		})
      		return {
      				setAvatar: function(data) {
      					console.log('设置header模块的头像')
      				}
      			}
      		}
      	)()
      	
      	let nav= (function() {
      		login.listen('loginSucc', data => {
      				nav.setAvatar(data.avatar)
      		})
      		return {
      				setAvatar: function(data) {
      					console.log('设置nav模块的头像')
      				}
      			}
      		}
      	)()
      
      	let address= (function() {
      		login.listen('loginSucc', data => {
      				address.refresh(data)
      		})
      		return {
      				refresh: function(data) {
      					console.log('刷新收货地址列表')
      				}
      			}
      		}
      	)()
      

五. 总结

发布-订阅模式是js最基本的设计模式之一,精髓在与设置一个缓存对象来保存回调函数,当触发相应事件后,执行回调函数。

可以看到使用发布订阅模式能够使我们的代码松耦合,便于代码维护和功能的拓展。

demo源码参考git:js-design-pattern

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值