js 发布订阅模式应用场景 自己手写一个简易的发布订阅模块

在这里插入图片描述

我们先写一个简单的发布订阅:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>发布订阅模式</title>
</head>
<body>
	<script type="text/javascript">
		var shopObj = {} // 定义发布者 卖家对象
		shopObj.list = [] // 缓存列表 存放订阅函数

		// 添加订阅者
		shopObj.listen = function (fn) {
			shopObj.list.push(fn)
		}

		// 发布消息
		shopObj.trigger = function () {
			for (var i=0, fn; fn = this.list[i++];) {
				fn.apply(this, arguments)
			}
		}

		// 添加第一个订阅者
		shopObj.listen(function(color, size) {
			console.log(`订阅者1:颜色是${color}, 尺码是${size}`)
		})

		// 添加第二个订阅者
		shopObj.listen(function(color, size) {
			console.log(`订阅者2:颜色是${color}, again尺码是${size}`)
		})

		// 发布第一个消息 本意是要通知第1个订阅者 橘黄色 42尺码的货到了
		shopObj.trigger('orange', 42)
		// 发布第二个消息 本意是要通知第2个订阅者 黑色 39尺码的货到了
		shopObj.trigger('black', 39)

	</script>
</body>
</html>

在这里插入图片描述

可以看到,已经可以订阅消息,并且发布消息,但是有个问题,就是发布消息的时候,把消息发送给所有订阅者了,怎么才能解决这个问题呢?

我们回忆一下,我们在使用发布订阅的时候,是不是这样使用的,要传入一个标识:
在这里插入图片描述
然后在触发时候,和订阅时候传入的标识对应上,是不是就能解决这个问题了

我们这样改造: 添加订阅时候,传入一个标识符key, 如果缓存列表里不存在对应的list[key],那么就创建一个对应的list[key]初始值是一个空数组[]

如果有的话,直接把回调函数push进入对应的list[key]
在这里插入图片描述
完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>发布订阅模式2-优化</title>
</head>
<body>
	<script type="text/javascript">
		var shopObj = {} // 定义发布者 卖家对象
		shopObj.list = [] // 缓存列表 存放订阅函数

		// 添加订阅者
		shopObj.listen = function (key, fn) {
			// 没有对应key的话就创建一个数组
			if (!this.list[key]) {
				this.list[key] = []
			}
			// 然后再把回调函数push进list[key]
			shopObj.list[key].push(fn)
		}

		// 发布消息
		shopObj.trigger = function () {
			// 获取对应的key
			var key = Array.prototype.shift.call(arguments) // 这里因为arguments是一个类数组 没有shift方法 需要借用
			var fns = this.list[key]
			if (!fns || fns.length === 0) {
				return
			}

			for (var i=0, fn; fn = fns[i++];) {
				fn.apply(this, arguments)
			}
		}

		// 添加第一个订阅者dean
		shopObj.listen('dean', function(color, size) {
			console.log(`订阅者dean:颜色是${color}, 尺码是${size}的鞋子到货了!`)
		})

		// 添加第二个订阅者jing
		shopObj.listen('jing', function(color, size) {
			console.log(`订阅者jing:颜色是${color}, again尺码是${size}的鞋子到货了!`)
		})

		// 发布第一个消息 通知第dean 橘黄色 42尺码的鞋子到货了
		shopObj.trigger('dean', 'orange', 42)


		// 发布第二个消息 通知第jing 黑色 39尺码的货到了
		shopObj.trigger('jing', 'black', 39)

	</script>
</body>
</html>

在这里插入图片描述

我们优化一下代码,再添加一个取消订阅:

var event = {
  list: [],
  listen: function(key, fn) {
    // 没有对应key的话就创建一个数组
    if (!this.list[key]) {
      this.list[key] = []
    }
    // 然后再把回调函数push进list[key]
    shopObj.list[key].push(fn)
  },
  trigger: function() {
    // 获取对应的key
    var key = Array.prototype.shift.call(arguments)
    var fns = this.list[key]
    if (!fns || fns.length === 0) {
      return
    }

    for (var i = 0, fn; fn = fns[i++];) {
      fn(...arguments)
    }
  },
  // 取消订阅
  remove: function(key, fn) {
    var fns = this.list[key]
    if (!fns) {
      return false
    } else {
      for (var i = fns.length - 1; i >= 0; i--) {
        var _fn = fns[i]
        if (_fn == fn) {
          fns.splice(i, 1)
        }
      }
    }
  }
}

代码还可以用立即执行函数封装一下:

// 代码封装成立即执行函数
var Event = (function() {
  var list = [],
    listen,
    trigger,
    remove;
  listen = function(key, fn) {
      // 没有对应key的话就创建一个数组
      if (!this.list[key]) {
        this.list[key] = []
      }
      // 然后再把回调函数push进list[key]
      shopObj.list[key].push(fn)
    },
    trigger = function() {
      // 获取对应的key
      var key = Array.prototype.shift.call(arguments)
      var fns = this.list[key]
      if (!fns || fns.length === 0) {
        return
      }

      for (var i = 0, fn; fn = fns[i++];) {
        fn(...arguments)
      }
    },
    remove = function(key, fn) {
      var fns = this.list[key]
      if (!fns) {
        return false
      } else {
        for (var i = fns.length - 1; i >= 0; i--) {
          var _fn = fns[i]
          if (_fn == fn) {
            fns.splice(i, 1)
          }
        }
      }
    }
  return {
    listen,
    trigger,
    remove
  }
})()

我们再看一下,发布订阅模式的使用场景: 解决异步调用中的强耦合问题:

在这里插入图片描述
在这里插入图片描述

这种强耦合代码,缺点是,修改一个地方,其他地方都要跟着一起修改

使用发布订阅模式修改:
在这里插入图片描述
在这里插入图片描述

我们接着在vue里面自己手写一个简易的发布订阅模块:

Main.vue

<template>
  <div>
    <el-input type="text" v-model="name" placeholder="Input name"></el-input>
    <el-button type="primary" @click="handlePub">Pub</el-button>
    <Son />
  </div>
</template>

<script>
import Son from './Son.vue'
import pubsub from './utils/pubsub2'
export default {
  name: 'Pub',
  components: {
    Son
  },
  data () {
    return {
      name: ''
    }
  },
  methods: {
    handlePub () {
      pubsub.trigger('item', this.name)
    }
  }
}
</script>

<style>

</style>

Son.vue

<template>
  <div>
    <h1>{{content}}</h1>
  </div>
</template>

<script>
import pubsub from './utils/pubsub2'
export default {
  name: 'Son',
  data () {
    return {
      content: ''
    }
  },
  mounted () {
    let _this = this // 这里要注意this指向 或者用箭头函数
    pubsub.listen('item', function(data) {
      console.log('data:', data)
      console.log('this:',this)
      _this.content = data
    })
  }
}
</script>

<style>

</style>

pubsub2.js

var Event = (function(){
  var list = [],
  listen,
  trigger,
  remove;
  listen = function (key, fn) {
    // 没有对应key的话就创建一个数组
    if (!list[key]) {
      list[key] = []
    }
    // 然后再把回调函数push进list[key]
    list[key].push(fn)
  },
  trigger = function () {
    // 获取对应的key
    var key = Array.prototype.shift.call(arguments)
    var fns = list[key]
    if (!fns || fns.length === 0) {
      return
    }

    // for (var i=0, fn; fn = fns[i++];) {
    //   fn(...arguments)
    // }

    for (var i = 0; i< fns.length; i++) {
      var fn = fns[i]
      fn(...arguments)
    }
  },
  remove = function (key, fn) {
    var fns = list[key]
    if (!fns) {
      return false
    } else {
      for (var i = fns.length - 1; i >= 0; i--) {
        var _fn = fns[i]
        if (_fn == fn) {
          fns.splice(i, 1)
        }
      }
    }
  }
  return {
    listen,
    trigger,
    remove
  }
})()

export default Event

在这里插入图片描述
效果:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值