vue源码之$once是如何实现的

1. 前言

之前研究了下观察者模式,但是事件注册后,都需要手动移除。就在琢磨,能否执行一次后,自动移除。正好vue有这一功能。于是,研究了下相关代码。

2.vue的$once

vue的事件处理代码放在了events.js文件中。它的$once长这样:

/**
 * Adds an `event` listener that will be invoked a single
 * time then automatically removed.
 *
 * @param {String} event
 * @param {Function} fn
 */

exports.$once = function (event, fn) {
  var self = this
  // on函数中先卸载,然后执行了fn
  function on () {
    self.$off(event, on)
    fn.apply(this, arguments)
  }
  on.fn = fn 
  this.$on(event, on)
  return this // 链式调用
}

代码不多,也比较容易理解。其内部依然用$on注册的。

  • 首先,我们是注册后,执行一次即移除,因此,明面上传入的fn一定不能直接给$emit()调用。因为,我们是需要调用一次后就移除,所以,给$emit()调用的函数,一定有移除事件的处理逻辑。因此,我们看到,内部的$on注册的是on函数,该函数内部有移除on函数的操作。
  • 其次,还需要在on函数内部执行一开始传入的fn函数。因此,就有了on函数内部,既要执行fn也要销毁注册的on函数,这样的一种机制。

3.实现自己的$once

const Observer = (function (type, fn) {
            const _msg = {};
            return {
                register: function (type, fn) {
                    if (!_msg[type]) {
                        _msg[type] = []
                    }
                    _msg[type].push(fn) // 先存储起来,供以后调用。回调
                },
                fire: function (type, ...args) {
                    if (_msg[type].length) {
                        _msg[type].forEach(element => {
                            element.apply(this, args)
                        });
                    } else {
                        console.log('无该订阅函数');
                    }
                },
                remove: function (type, fn) {
                    if (_msg[type] && _msg[type].includes(fn)) {
                        _msg[type].splice(_msg[type].indexOf(fn), 1)
                    } else {
                        console.log('无该订阅函数');
                    }
                },
                registerOnce: function(type, fn) { // 注册一次,fire()后销毁掉
                    const self = this
                    function on() {
                        fn.apply(this, arguments) // 执行的是fn
                        self.remove(type, on) // 注意,卸载的是on函数(利用了闭包)
                    }
                    this.register(type, on) // on里边必须要有销毁函数
                }
            }
        })()

        Observer.registerOnce('change', (type, data) => {
            console.log(type, data);
        })

        Observer.fire('change', 'change', 1111)

        Observer.fire('change', 'change', 2)

结果如下:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值