前端EventEmitter,发布/订阅模式,Vue双向数据绑定

本文主要是介绍前端EventEmitter,发布/订阅模式,Vue双向数据绑定,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前端EventEmitter,发布/订阅模式

  • 前言
  • 实现
  • 拓展
    • Vue 中非父子组件组件通信
    • 通俗易懂了解Vue双向绑定原理及实现
    • Vue双向绑定原理,教你一步一步实现双向绑定
  • 参考的代码1
  • 参考代码2`推荐`
  • [Source Code - JavaScript - 学习优雅的编码](https://segmentfault.com/a/1190000015043262)
  • 参考

前言

发布订阅模式,很多地方都用到的一种模式,简单的说就是预定一件事情,时机成熟通知你,比如我们nodejs中的fs的读写文件的流,消息事件的触发等都用到了这种方式了,虽然不是特别难,但是非常实用,实现方式也简单,基本思想就是内部保存了一个对象存储订阅的函数,调用者通过名字来触发函数,订阅多个就按照队列的形式触发。

DOM 的事件机制就是发布订阅模式最常见的实现,这大概是前端最常用的编程模型了,监听某事件,当该事件发生时,监听该事件的监听函数被调用。

实现

class EventEmitter {constructor() {this.events = Object.create(null)}on(type, handler) {;(this.events[type] || (this.events[type] = [])).push(handler)}off(type, handler) {if (this.events[type]) {this.events[type].splice(this.events[type].indexOf(handler) >>> 0, 1)}}emit(type) {let args = [].slice.call(arguments, 1)let array = this.events[type] || []array.forEach(cb => {cb.apply(this, args)})}once(type, handler) {function _fn() {handler.apply(this, arguments)this.off(type, _fn)}this.on(type, _fn)}
}
export default EventEmitter

使用

let em = new EventEmitter()function fn(price) {console.log('price', price)
}em.once('work', fn)
em.off('work', fn)
em.emit('work', 100)console.log(em)

拓展

Vue 中非父子组件组件通信

在 Vue 中不同组件之间通讯,有一种解决方案叫Event Bus,这其实就是发布订阅模式的实现,非常简单好用。
在这里插入图片描述

通俗易懂了解Vue双向绑定原理及实现

Vue双向绑定原理,教你一步一步实现双向绑定

在正式开始之前我们先来说说数据绑定的事情,数据绑定我的理解就是让数据M(model)展示到 视图V(view)上。我们常见的架构模式有 MVC、MVP、MVVM模式,目前前端框架基本上都是采用 MVVM 模式实现双向绑定,Vue 自然也不例外。但是各个框架实现双向绑定的方法略有所不同,目前大概有三种实现方式。

  • 发布订阅模式
  • Angular 的脏查机制
  • 数据劫持

而 Vue 则采用的是数据劫持与发布订阅相结合的方式实现双向绑定,数据劫持主要通过 Object.defineProperty来实现。

所谓MVVM数据双向绑定,即主要是:数据变化更新视图,视图变化更新数据.

实现Vue的数据双向绑定,需要如下:

  • Observer 监听器:用来监听属性的变化通知订阅者
  • Watcher 订阅者:收到属性的变化,然后更新视图
  • Dep 订阅器: 负责收集订阅者,然后当数据变化的时候后执行对应订阅者的更新函数。
  • Compile 解析器:解析指令,初始化模版,绑定订阅者

总结:

实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。
在这里插入图片描述
在这里插入图片描述

参考的代码1

这个有问题,没有传参数, 需要自行修改一下

// 发布订阅模式
class EventEmitter {constructor() {// 事件对象,存放订阅的名字和事件this.events = {};}// 订阅事件的方法on(eventName,callback) {if (!this.events[eventName]) {// 注意时数据,一个名字可以订阅多个事件函数this.events[eventName] = [callback]} else  {// 存在则push到指定数组的尾部保存this.events[eventName].push(callback)}}// 触发事件的方法emit(eventName) {// 遍历执行所有订阅的事件this.events[eventName] && this.events[eventName].forEach(cb => cb());}// 移除订阅事件removeListener(eventName, callback) {if (this.events[eventName]) {this.events[eventName] = this.events[eventName].filter(cb => cb != callback)}}// 只执行一次订阅的事件,然后移除once(eventName,callback) {// 绑定的时fn, 执行的时候会触发fn函数let fn = () => {callback(); // fn函数中调用原有的callbackthis.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次}this.on(eventName,fn)}
}

使用方式

let em = new EventEmitter();
let workday = 0;
em.on("work", function() {workday++;console.log("work everyday");
});em.once("love", function() {console.log("just love you");
});function makeMoney() {console.log("make one million money");
}
em.on("money",makeMoney)let time = setInterval(() => {em.emit("work");em.removeListener("money",makeMoney);em.emit("money");em.emit("love");if (workday === 5) {console.log("have a rest")clearInterval(time);}
}, 1);

参考代码2推荐

推荐看这个
在这里插入图片描述

Source Code - JavaScript - 学习优雅的编码

  // source codeall = all || Object.create(null);

Object.create(null):生成的对象是一个原型为空的对象。节约内存且避免冲突,因为没有原型,且普通对象原型上的属性和方法也相应没有了。

  // source code(all[type] || (all[type] = [])).push(handler);// my code - badif (all[type]) {all[type].push(handler)} else {all[type] = [handler]}

简洁的队列赋值:短路逻辑判断 + 初始化 + 更新数组,简直不要太优雅。

  // source codeall[type].splice(all[type].indexOf(handler) >>> 0, 1);
  • 按位操作符:1 >>> 0 = 1, -1 >>> 0 = 4294967295, 详情MDN
  • 补充:按位操作符~,可以结合.indexOf()使用,因为对任一数值 x 进行按位非操作的结果为 -(x + 1),即:~-1 = 0
  // source code(all[type] || []).slice().map((handler) => { handler(evt); });(all['*'] || []).slice().map((handler) => { handler(type, evt); });
  // source code!!(0)             // false!!(null)          // false!!('')            // false!!(undefined)     // false!!(NaN)           // false!!(2)             // true
  • !!: 强制转换成 boolean 类型,相当于 !(!val)。如果 val = 0/null/""/undefined/NaN 时,!!(val) = false,如果 val 是其他值,!!(val) = true
  • +: +val将字符串数字转为数字。如果 val 是非字符串数字,则 +val = NaN
  // source code+'123456'        // 123456, Number+new Date()      // 1527684413484, 相当于 new Date().getTime()

参考

TypeScript/理解Event Emitter (事件派发器)推荐
Understanding Event Emitters
上面案例的github代码
一个例子 - 看尽并手写JS发布订阅模式
前端必懂EventEmitter,不懂会丢人

这篇关于前端EventEmitter,发布/订阅模式,Vue双向数据绑定的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/890746

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06