本系列中会用到的第三方库
一、起因
上一篇文章中说过,在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、方法定义
讲完了数据的存储,那么就该讲讲需要定义的几个方法了,暂时先讲$emit
、on
、$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操作
等,之后会在对应的文章中详细说明。
组件库的开发会更偏向于通用组件库的开发方式,而我学习、开发这个组件库的初衷是为了在我的个人项目中使用,所以我会针对自己的项目需求进行二次封装,以适应我的个人项目。
这里多少有一点脱了裤子的味道,但是我想做的是:即开发一个通用组件库,也能完美的适用于我的个人项目
。这二者肯定是冲突的,所以我才会采用对部分组件进行二次封装的方式来处理。
此外,会单独拿出文章去阐述,在针对部分情况下,为了使用项目需求,我是如何去二次封装一个通用组件的。
这个专栏的目的,其实还是为了记录、分享、表达我是如何去学习开发一个组件
,如何开发一个组件库
,以及如何在风格、特点比较强的项目中对一个通用组件库进行组件二次封装
。
希望没有辜负你的期待,并且期盼你能继续看下去。