Vue3从0到1开发组件前戏(中):$emit,$on,$off替代方案

本系列中会用到的第三方库

一、起因

上一篇文章中说过,在Vue的新版本,Vue3中删除了$on,$once,$off等方法,$emit仍然是现有 API 的一部分,因为它用于触发由父组件以声明方式附加的事件处理程序。

但是既然是组件开发,那么组件与组件之间的通信,组件与父级之间的通信肯定是少不了的,$emit方法虽然已经能满足半数以上的通信需求,但是剩下满足不了的部分,就是满足不了。所以就要用到市面上的第三方库来作为平替方案了。

Vue3迁移文档中提及:在 Vue 3 中,已经不可能使用这些 API 从组件内部监听组件自己发出的事件了,该用例暂没有迁移的方法。。但是在使用第三方库之前,我还是挣扎了一下,用其他方式去完成预期的功能,但是最终结合性能、复杂度等方面来考虑,还是通过引入第三方库的方式来使用这几个方法。

不过值得一提的是,如果只考虑当前项目,可以使用Vuex来实现,但是一个组件库不好自带VueX到处跑,所以最终还是使用了mitt,这个库不依赖某个框架,其本身的实现也比较简单。

但是不好对方法、事件进行溯源,所以各位在实际开发中,就需要根据具体情况进行选择了,仁者见仁。

作者还是以学习为主,虽然$emit等几个方法已经用了很多次了,但是自己按照别人的库手抄一遍并且去理解其核心原理还是首次。

下文就简单讲讲对其中几个方法实现的简单理解。

如果看不懂也没关系,我会尽量锻炼我的文笔,并且在后面的组件开发过程中去深入讲解,以及分享一些使用心得。

一库:mitt.js $on等方法的平替

别问我为什么用一库, 懂的都懂。

选择mitt.js这个库原因有二,其一是我在学习组件开发的时候,抄的开源库relax-plus(gitee)中的mitt库的写法,对比原仓库中的mitt开源代码进行了一些修改,所以在我在开发过程中也一直是在使用这个库。其二这也是Vue3在迁移文档中点名推荐的平替方案。

1、数据保存

将mitt方法要做的事做个比喻,该库的作用好比一辆公交车,上面的东西任意取用($on),如果你有什么信息需要传达出去也可以将至放在($emit)车上;如果不想公开了也可以把它撤下来($off)。

当然,最终可实现的方法肯定不只这几个, 但是这差不多是最核心的一个作用了。其他的方法在需要的地方也会一一提到。

先来说说这辆“”是如何保存货物的。

这里用到的是ES6带来的新的数据结构Map了,相比起传统的对象的键值对方式, Map采用的“值-值”的方式存储数据,更加实用,也更加灵活。

而在mitt库中,实用Map数据结构来作为数据的存储方式,相较传统的对象而言:

1、频繁增删操作Map会有些性能优势。

2、Map的键名类型更加自由,Object仅支持String,Symbol类型作为键名。

3、Map更加干净,默认情况下不包含任何键名,所有键可自主添加。

2、方法定义

讲完了数据的存储,那么就该讲讲需要定义的几个方法了,暂时先讲$emiton$off这三个方法,之后在需要的地方再拓展其他的一些方法。

先从$emit方法开始,此方法的主要作用是声明一个事件名称并绑定需要传递的参数,之后丢到公共池中(即前面提到的车);

$on方法则是在事件池中监听对应的方法名,并在触发之后执行指定方法。

$off方法卸载监听,比较简单,在事件池中找到对应的方法,并通过移除的方式取消监听。

// 使用方法
$emit('eventName', argsobj);
$on('eventName', fn);
$off('eventName', fn);

上面是$emit等几个方法的简单使用,只接受一个参数, 可以是基本类型或引用类型。到此为止,对于简单使用以及要达成的效果有了基本认知了,那么可以按照这个思路去实现相应的函数功能了。

实现方法如下代码,原理较简单,通过es6的Map结构保存函数,并通过Map的几个属性操作方法,以及一个缓存库完成对事件的处理。

export default function() {
  const all = new Map()
  const cached = {}
  
  return {
    emit(type, evt) {
      ;(all.get(type) || []).slice().map((handler) => {
        handler(evt)
      })
      cached[type] = Array.prototype.slice.call(arguments, 1)
    },

    on(type, handler) {
      const handlers = all.get(type)
      const added = handlers && handlers.push(handler)
      if (!added) {
        all.set(type, [handler])
      }

      if (cached[type] instanceof Array) {
        handler.apply(null, cached[type])
      }
    },

    off(type, handler) {
      const handlers = all.get(type)
      if (handlers) {
        handlers.splice(handlers.indexOf(handler), 1)
      }
    }
  }
}

多说几句

第三方库用的不多,后面会对mitt.js进行二次封装,同时在开发过程中,也会抽出一些常用的工具函数。

包括类型判断DOM操作等,之后会在对应的文章中详细说明。

组件库的开发会更偏向于通用组件库的开发方式,而我学习、开发这个组件库的初衷是为了在我的个人项目中使用,所以我会针对自己的项目需求进行二次封装,以适应我的个人项目。

这里多少有一点脱了裤子的味道,但是我想做的是:即开发一个通用组件库,也能完美的适用于我的个人项目。这二者肯定是冲突的,所以我才会采用对部分组件进行二次封装的方式来处理。

此外,会单独拿出文章去阐述,在针对部分情况下,为了使用项目需求,我是如何去二次封装一个通用组件的。

这个专栏的目的,其实还是为了记录、分享、表达我是如何去学习开发一个组件如何开发一个组件库,以及如何在风格、特点比较强的项目中对一个通用组件库进行组件二次封装

希望没有辜负你的期待,并且期盼你能继续看下去。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值