前端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

相关文章

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

SQL中如何添加数据(常见方法及示例)

《SQL中如何添加数据(常见方法及示例)》SQL全称为StructuredQueryLanguage,是一种用于管理关系数据库的标准编程语言,下面给大家介绍SQL中如何添加数据,感兴趣的朋友一起看看吧... 目录在mysql中,有多种方法可以添加数据。以下是一些常见的方法及其示例。1. 使用INSERT I

Python使用vllm处理多模态数据的预处理技巧

《Python使用vllm处理多模态数据的预处理技巧》本文深入探讨了在Python环境下使用vLLM处理多模态数据的预处理技巧,我们将从基础概念出发,详细讲解文本、图像、音频等多模态数据的预处理方法,... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

Navicat数据表的数据添加,删除及使用sql完成数据的添加过程

《Navicat数据表的数据添加,删除及使用sql完成数据的添加过程》:本文主要介绍Navicat数据表的数据添加,删除及使用sql完成数据的添加过程,具有很好的参考价值,希望对大家有所帮助,如有... 目录Navicat数据表数据添加,删除及使用sql完成数据添加选中操作的表则出现如下界面,查看左下角从左