Vue中的发布订阅分析(Vue2/3中的 emit 实现)
Vue中的发布订阅模式分析
模块:instanceEventEmiiter.ts(在下方有简单实现和解析)
在Vue3中,已经取消了对这个模块的引用,故而不再支持 $on、$off、$once相关的方法,不过还是可以对进行学习和借鉴,运用到工作中。
Vue3中的简单实现
Vue3中 emit 的实现相对 Vue2 来说更加简单一些了,他是通过 h函数 的第二个参数来实现的
实现 Child 组件
const { createApp, h } = Vue
// 创建一个子组件
const Child = {
setup(props, ctx) {
return {
buttonClick() {
// 源码中挂载 emit 的函数:createComponentInstance
// 挂载代码:instance.emit = emit.bind(null, instance)
// emit函数接受3个参数: emit(instance, event, ...rawArgs)
// 其中 instance 在挂载到 instance 的时候默认传入了
// ctx.emit('test') 相当于 ctx.emit('当前Vue实例', 'test', 123)
// emit函数中会对 on开头、 Once 结尾等关键字符串进行解析
// 然后从 instance.vnode.props 中去获取对应的回调函数
// 然后执行 回调函数,此次发布订阅流程也就完成了 // 执行 emit 方法发布一个 test 事件
ctx.emit('test', 123)
}
}
},
render(e) {
return h('button', {
onClick: this.buttonClick
}, '派发Emit')
}
}
实现App组件
const App = {
render(e) {
// h函数返回一个 vnode
// 当 h 函数的第二个参数是一个 Object,并且不是一个 VNode 时
// vnode.props 就是 h 函数的第二个参数值
return h(
'div', {}, [
// 引用 Child 组件,并且传入 onTest 方法
h(Child, {
// 监听 test 事件 vnode.props = { onText: Funtion }
onTest(e) {
console.log('emit test event!', e)
}
})
]
)
}
} createApp(App).mount(document.getElementById('emitApp'))
instanceEventEmiiter 实现代码(简易 JS 版本)
· 其中像 WeakMap对象、getRegistry方法的运用,及对与 eventName 为数组的处理逻辑还是值得学习的
· 在工作中可以在 Vue3 的项目中实现一个 Emiiter 模块,代替 Vue2中的 bus 实现,用于全局事件通讯
// 发布订阅仓库
// 数据结构类似: { Instance, Object<[event: string]: Function[] | undefined> }
const eventRegistryMap = new WeakMap() /**
* 根据 instance 进行事件注册
* @param {*} instance 当前 Vue 实例对象
* @returns 当前事件对象s
*/
function getRegistry(instance) {
// 从 map 中获取事件对象
let events = eventRegistryMap.get(instance) if(!events) {
// 若没有注册过,则进行新注册
eventRegistryMap.set(instance, (events = Object.create(null)))
} // 返回 实例的事件对象
return events
} /**
* 添加订阅
* @param {*} instance 实例
* @param {*} eventName 事件名称 string | string[]
* @param {*} fn 回调函数 function
*/
function on(instance, eventName, fn) {
if(Array.isArray(eventName)) {
// 处理 eventName 为 数组的情况
//(如:this.$on(['tab:click', 'tab:change'], () => {}))
eventName.forEach(c => on(instance, c, fn))
} else{
// 获取 instance实例 的事件对象的
let events = getRegistry(instance)
// 将回调函数添加到事件对象的 回调函数 列表中
(events[eventName] || (events[eventName] = [])).push(fn)
}
} /**
* 发布订阅
* @param {*} instance 实例
* @param {*} eventName 事件名称 string | string[]
* @param {*} args 回调函数 function
*/
function emit(instance, eventName, args) {
// 获取 instance实例 中事件名称为 eventName 的回调函数列表
const cbs = getRegistry(instance)[eventName] if(cbs) {
// 在 Vue 源码中,这里是通过 callWithAsyncErrorHandling 函数统一进行执行的
// 目的是为了统一收集回调函数中的异常 // 执行回调函数(这里我们直接调用了)
cbs.forEach(fn => {
fn.apply(instance.proxy, args)
})
}
} /**
* 添加订阅(只触发一次)
* @param {*} instance 实例
* @param {*} eventName 事件名称 string | string[]
* @param {*} fn 回调函数 function
* @returns
*/
function once(instance, eventName, fn) {
// 由于只需要执行一次,这里对 回调 函数进行了包装
const wrapped = (...args) => {
// 执行回调是我们需要吧他删除掉
off(instance, eventName, fn)
// 执行回调函数
fn.call(instance.proxy, ...args)
} // 在包装函数上增加一个 fn属性,用于删除这个回调函数时做匹配
wrapped.fn = fn
// 添加到 订阅 列表中
on(instance, eventName, wrapped)
return instance.proxy
} /**
* 移除订阅事件
* @param {*} instance 实例
* @param {*} eventName 事件名称 string | string[]
* @param {*} fn 回调函数 function
* @returns
*/
function off(instance, eventName, fn) {
if(Array.isArray(eventName)) {
// 处理 eventName 为 数组的情况
eventName.forEach(c => off(instance, c, fn))
} else {
const vm = instance.proxy
const events = getRegistry(instance)
const cbs = events[eventName]
// 事件 订阅列表 为空时提前处理
if(!cbs) {
return vm
} // 没有传入回调函数,直接将 事件订阅列表 清空即可
if(!fn) {
events[eventName] = undefined
return vm
} // 过滤掉需要清除的回调,重新赋值给这个事件的 订阅列表
events[eventName] = cbs.filter(c => !(c === fn))
}
}
成功之前我们要做应该做的事情,成功之后我们才可以做喜欢做的事情
Vue中的发布订阅分析(Vue2/3中的 emit 实现)的更多相关文章
- JS中的发布订阅模式
一. 你是如何理解发布订阅模式的 JS中的设计模式: 单例模式:处理业务逻辑 构造原型模式:封装类库,组件,框架,插件等 类库:jQuery 只是提供了一些常用的方法,可以应用到任何的项目中,不具备业 ...
- Javascript中理解发布--订阅模式
Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...
- [转] Javascript中理解发布--订阅模式
发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布- ...
- redis中的发布订阅(Pub/Sub)
这里使用nodejs的redis模块说明,具体可见https://www.npmjs.com/package/redis,先来通过一个简单的例子了解下redis中的Pub/Sub具体怎么实现吧.. v ...
- 【转】Javascript中理解发布--订阅模式
Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时 ...
- javascript中的发布订阅模式与观察者模式
这里了解一下JavaScript中的发布订阅模式和观察者模式,观察者模式是24种基础设计模式之一. 设计模式的背景 设计模式并非是软件开发的专业术语,实际上设计模式最早诞生于建筑学. 设计模式的定义是 ...
- python开发-实现redis中的发布订阅功能
Python3学习(二十七):python实现Redis的订阅与发布(sub-pub机制) 先介绍一下redis的pub/sub功能: Pub/Sub功能(means Publish, Subscri ...
- RedisRepository封装—Redis发布订阅以及StackExchange.Redis中的使用
本文版权归博客园和作者本人吴双共同所有,转载请注明本Redis系列分享地址.http://www.cnblogs.com/tdws/tag/NoSql/ Redis Pub/Sub模式 基本介绍 Re ...
- OrcharNoCMS中的发布订阅使用
对于Orchard里面的EventBus,没有太多的文章去介绍说明.它最好的应用是发布订阅的应用. 使用介绍: 在Car模块中,我们定义一个接口,继承IEventHandler接口. 当我们在创建一条 ...
随机推荐
- 【LeetCode】482. License Key Formatting 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- 【剑指Offer】二叉搜索树的第k个结点 解题报告(Python)
[剑指Offer]二叉搜索树的第k个结点 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-intervie ...
- MySQL 尽量避免使用 TIMESTAMP
MySQL 中常见的时间类型有三种DATE, DATETIME和 TIMESTAMP,其中DATE类型用于表示日期,但是不会包含时间,格式为YYYY-MM-DD,而DATETIME和TIMESTAMP ...
- isEmpty 和 isBlank
<org.apache.commons.lang3.StringUtils> isEmpty系列 StringUtils.isEmpty() ========> StringUtil ...
- Mysql数据库语言学习的路线
对于我们数据库的学习,不管是测试人员还是开发人员以及我们的DBA来说重点都是SQL:但是我们的SQL可以分多少类型,学习重点又是在哪里呢,本文仅仅针对测试人员来展开说明: SQL:structure ...
- VS Code 如何设置大小写转换快捷键
一般情况下,快捷键如下: 转换为大写:Ctrl+Shift+u 转换为小写:Ctrl+Shift+l 如果不行的话,需要单独进行设置,步骤如下: 1.点击[文件]-[首选项]-[键盘快捷方式]菜单: ...
- vue路由history模式刷新404问题解决方案
更改router 的base // biz是二级目录,路由文件改成 const router = new VueRouter({ mode: 'history', // base: process.e ...
- [数据结构]链表相关的实现LinkList.cpp
目录 LinkList.cpp //链表相关操作的实现 LinkList.h LinkListManager.cpp //链表相关实现函数的调用 LinkListManager.h LinkList. ...
- 『无为则无心』Python函数 — 29、Python变量和参数传递
目录 1.Python的变量 (1)Python变量不能独立存在 (2)变量是内存中数据的引用 (3)注意点 2.了解变量的引用 3.Python的参数传递(重点) (1)示例 (2)结论 (3)总结 ...
- JUC之多线程锁问题
多线程锁 8种问题锁状态: 该部分全部围绕的是以下内容并结合相应的例子:synchronized实现同步的基础:Java中每个对象都可以作为锁. 具体表现为以下三种形式:(之前只是简单的了解) 对于普 ...