前言
除了大家经常提到的自定义事件之外,浏览器本身也支持我们自定义事件,我们常说的自定义事件一般用于项目中的一些通知机制。最近正好看到了这部分,就一起看了下自定义事件不同的实现,以及vue数据响应的基本原理。
浏览器自定义事件
定义
除了我们常见的click,touch等事件之外,浏览器支持我们定义和分发自定义事件。
创建也十分简单:
1 | //创建名为test的自定义事件 |
大多数现代浏览器对new Event/CustomEvent 的支持还算可以(IE除外),可以看下具体情况:
可以放心大胆的使用,如果非要兼容IE那么有下面的方式
1 | var event = document.createEvent('Event'); |
自定义事件的触发和原生事件类似,可以通过冒泡事件触发。
1 | <form> |
触发如下,这里就偷个懒,直接拿mdn的源码来示例了,毕竟清晰易懂。
1 | const form = document.querySelector('form'); |
上面例子很清晰的展示了自定义事件定义、监听、触发的整个过程,和原生事件的流程相比看起来多了个触发的步骤,原因在原生事件的触发已经被封装无需手动处理而已。
应用
各大js类库
各种js库中用到的也比较多,例如zepto中的tap,原理就是监听touch事件,然后去触发自定的tap事件(当然这种成熟的框架做的是比较严谨的)。可以看下部分代码:
1 | //这里做了个event的map,来将原始事件对应为自定义事件以便处理 |
发布订阅
和原生事件一样,大部分都用于观察者模式中。除了上面的库之外,自己开发过程中用到的地方也不少。
举个例子,一个输入框表示单价,另一个div表示五本的总价,单价改变总价也会变动。借助自定义事件应该怎么实现呢。
html结构比较简单
1 | <div >一本书的价格:<input type='text' id='el' value=10 /></div> |
当改变input值得时候,效果如下demo地址 :
大概思路捋一下:
1、自定义事件,priceChange,用来监听改变price的改变
2、 加个监听事件,priceChange触发时改变total的值。
3、input value改变的时候,触发priceChange事件
代码实现如下:
1 | const count = document.querySelector('#el'), |
代码确实比较简单,当然实现的方式是多样的。但是看起来是不是有点vue数据响应的味道。
确实目前大多数框架中都会用到发布订阅的方式来处理数据的变化。例如vue,react等,以vue为例子,我们可以来看看其数据响应的基本原理。
自定义事件
这里的自定义事件就是前面提到的第二层定义了,非基于浏览器的事件。这种事件也正是大型前端项目中常用到。对照原生事件,应该具有on、trigger、off三个方法。分别看一下
- 对照原生事件很容易理解,绑定一个事件,应该有对应方法名和回调,当然还有一个事件队列
1 | class Event1{ |
- 触发事件trigger
1 | // 触发事件 |
- 解除绑定:
1 | // 取消绑定,还是循环查找 |
这样一个简单的事件系统就完成了,结合这个事件系统,我们可以实现下上面那个例子。
html不变,绑定和触发事件的方式改变一下就好
1 | // 初始化 event1为了区别原生Event |
这样同样可以实现上面的效果,实现了事件系统之后,我们接着实现一下vue里面的数据响应。
vue的数据响应
说到vue的数据响应,网上相关文章简直太多了,这里就不深入去讨论了。简单搬运一下基本概念。详细的话大家可以自行搜索。
基本原理
直接看图比较直观:
就是通过观察者模式来实现,不过其通过数据劫持方式实现的更加巧妙。
数据劫持是通过Object.defineProperty()来监听各个属性的变化,从而进行一些额外操作。
举个简单例子:
1 | let a = { |
所谓数据劫持就是在get/set操作时加上额外操作,这里是加了些log,如果在这里去监听某些属性的变化,进而更改其他属性也是可行的。
要达到目的,应该对每个属性在get是监听,set的时候出发事件,且每个属性上只注册一次。
另外应该每个属性对应一个监听者,这样处理起来比较方便,如果和上面那样全放在一个监听实例里面,有多个属性及复杂操作时,就太难维护了。
1 | //基本数据 |
可以对自定义事件进行部分改造,
不需要显式指定type,全局维护一个标记即可
事件数组一维即可,因为是每个属性对应一个示例
1 | class Events { |
对应上图中vue的Data部分,就是实行数据劫持的地方
1 | Object.keys(data).forEach((key) => { |
此时数据劫持即事件监听准备完成,大家可能会发现callback始终为null,这始终不能起作用。为了解决该问题,下面的watcher就要出场了。
1 | function watcher(func) { |
这样就保证了会将监听事件挂载上去。到这里,乞丐版数据响应应该就能跑了。
再加上dom事件的处理,双向绑定也不难实现。
可以将下面的完整代码放到console台跑跑看。
1 | let data = { |
结束语
参考文章
vue数据响应的实现
Creating and triggering events
看到知识盲点,就需要立即行动,不然下次还是盲点。正好是事件相关,就一并总结了下发布订阅相关进而到了数据响应的实现。个人的一点心得记录,分享出来希望共同学习和进步。更多请移步我的博客
demo地址
源码地址