Angular 个人深究(二)【发布与订阅】
Angular 个人深究(二)【发布与订阅】
1. 再入正题之前,首先说明下[ 发布与订阅模式](也叫观察者模式)
1) 定义:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时会通知所有观察者对象,使它们能够自动更新。
2) 结构图:
3) 解释:Subject类,可翻译为主题或抽象的通知者,一般是抽象类或者接口。Observer是抽象观察者,为所有的具体观察者定义一个接口。ConcreteSubject类,叫做具体通知者。ConcreteObserver是具体的观察者。
5) 代码实现:(用TypeScript实现)
/* 代码使用Typescript 编写,Angular中使用的他,需要编译成Javascript代码然后使用node js 运行*/ //抽象观察者
abstract class abstract_observer{
name:string;
message:string;
constructor(name:string){
this.name = name;
}
abstract handle_publish(msg);
}
//抽象主题
abstract class abstract_subject{
subject_name:string;
observers: abstract_observer[]= [];
current_message:string;
constructor(name:string){
this.subject_name = name;
}
//提供订阅入口
subscribe(observer:abstract_observer){
this.observers.push(observer);
console.log(observer.name+"成功订阅"+this.subject_name);
}
//提供取消订阅入口
unsubscribe(observer:abstract_observer ){
this.observers.splice(this.observers.indexOf(observer),1);
console.log(observer.name+"成功取消订阅"+this.subject_name);
}
update_message(msg){
this.current_message = msg;
}
//发布消息
publish(){
console.log("Server 开始发布消息");
for (let observer of this.observers){
console.log("发布消息给"+observer.name+"!");
observer.handle_publish(this.current_message);
}
console.log("所有订阅"+this.subject_name+"的人已经收到消息!");
}
}
//具体观察者
class concrete_observer extends abstract_observer{
constructor(name:string){
super(name);
}
handle_publish(msg){
this.message = msg;
console.log(this.name+": 已经接到消息:"+this.message);
}
}
//具体股票主题
class concrete_subject_gupiao extends abstract_subject{
publish(){
console.log('发送股票新消息');
super.publish();
}
update_message(updatemsg){
console.log("股票消息更新:"+updatemsg);
super.update_message(updatemsg);
}
}
//具体NBA主题
class concrete_subject_nba extends abstract_subject{
publish(){
console.log('发送NBA新消息');
super.publish();
}
update_message(updatemsg){
console.log("NBA消息更新:"+updatemsg);
super.update_message(updatemsg);
}
}
//订阅发布主逻辑
var observer1 = new concrete_observer("小明");
var observer2 = new concrete_observer("小强");
var observer3 = new concrete_observer("小红");
var subject_gupiao = new concrete_subject_gupiao("股票");
var subject_nba = new concrete_subject_nba("NBA");
console.log("小明订阅了股票");
subject_gupiao.subscribe(observer1);
console.log("小明订阅了NBA");
subject_nba.subscribe(observer1);
console.log("小强订阅了股票");
subject_gupiao.subscribe(observer2);
console.log("小红订阅了NBA");
subject_nba.subscribe(observer3);
console.log("---------------------------------");
subject_gupiao.update_message("大盘下跌");
subject_nba.update_message("骑士总冠军");
console.log("---------------------------------");
subject_gupiao.publish();
console.log("---------------------------------");
subject_nba.publish();
console.log("---------------------------------");
console.log("小明取消订阅股票");
subject_gupiao.unsubscribe(observer1);
console.log("小明取消订阅NBA");
subject_nba.unsubscribe(observer1);
console.log("---------------------------------");
subject_gupiao.update_message("大盘上涨");
subject_nba.update_message("骑士蝉联总冠军");
console.log("---------------------------------");
subject_gupiao.publish();
console.log("---------------------------------");
subject_nba.publish();
console.log("---------------------------------");
输入结果如下:
小明订阅了股票
小明成功订阅股票
小明订阅了NBA
小明成功订阅NBA
小强订阅了股票
小强成功订阅股票
小红订阅了NBA
小红成功订阅NBA
---------------------------------
股票消息更新:大盘下跌
NBA消息更新:骑士总冠军
---------------------------------
发送股票新消息
Server 开始发布消息
发布消息给小明!
小明: 已经接到消息:大盘下跌
发布消息给小强!
小强: 已经接到消息:大盘下跌
所有订阅股票的人已经收到消息!
---------------------------------
发送NBA新消息
Server 开始发布消息
发布消息给小明!
小明: 已经接到消息:骑士总冠军
发布消息给小红!
小红: 已经接到消息:骑士总冠军
所有订阅NBA的人已经收到消息!
---------------------------------
小明取消订阅股票
小明成功取消订阅股票
小明取消订阅NBA
小明成功取消订阅NBA
---------------------------------
股票消息更新:大盘上涨
NBA消息更新:骑士蝉联总冠军
---------------------------------
发送股票新消息
Server 开始发布消息
发布消息给小强!
小强: 已经接到消息:大盘上涨
所有订阅股票的人已经收到消息!
---------------------------------
发送NBA新消息
Server 开始发布消息
发布消息给小红!
小红: 已经接到消息:骑士蝉联总冠军
所有订阅NBA的人已经收到消息!
---------------------------------
说明:实现了订阅与发布不同主题的情况,并可以取消订阅。
4) 分享一个设计模式很好的书籍【大话设计模式——程杰】
百度网盘下载地址: https://pan.baidu.com/s/1iyUe5p0yAHNq3Hw2Z4L1Qg 提取码:n1pd
感兴趣的可以去看看。
2.下面开始说明Angular中的发布与订阅
注:本人在使用angular时,为了实现父子组件之间的变量传递,才研究到了其中的发布与订阅
功能实现:子组件更改数据到父组件(方法一)
//test1.component.ts
import { Component, OnInit } from '@angular/core';
import {EmitserviceService} from '../emitservice.service';
@Component({
selector: 'app-test1',
templateUrl: './test1.component.html',
styleUrls: ['./test1.component.css']
})
export class Test1Component implements OnInit {
data_from_child:string;
constructor(private emitter:EmitserviceService) {}
ngOnInit() {
this.emitter.emitter.subscribe((data)=>{
console.log(data);
this.data_from_child = data;
});
}
}
//test2.component.ts
import { Component, OnInit,EventEmitter } from '@angular/core';
import {EmitserviceService} from '../emitservice.service';
@Component({
selector: 'app-test2',
templateUrl: './test2.component.html',
styleUrls: ['./test2.component.css']
})
export class Test2Component implements OnInit {
constructor(private emitter:EmitserviceService) {}
ngOnInit() {
}
onTest(value:string){
this.emitter.emitter.emit(value);
}
}
//emitservice.service.ts
import { Injectable,EventEmitter } from '@angular/core'; @Injectable({
providedIn: 'root'
})
export class EmitserviceService {
public emitter:any;
constructor() {
this.emitter = new EventEmitter ;
}
}
//test1component.html
<div>
<div><h1>这是父组件test1</h1></div>
<h2>子组件的数据(子到父)</h2>
<h3>显示子组件的数据:{{data_from_child}}</h3>
<div>
<app-test2></app-test2>
</div>
</div>
//test2.component.html
<div><h1>这是子组件test2</h1></div>
输入数据传递到父组件:
<input type="text" (change)="onTest($event.target.value)">
底层代码探究:
需要用到的文件有:
\node_modules\@angular\core\fesm2015\core.js
\node_modules\rxjs\_esm5\internal\Subject.js
\node_modules\rxjs\_esm5\internal\Subscriber.js
\node_modules\rxjs\_esm5\internal\Observable.js
\node_modules\rxjs\_esm5\internal\util\toSubscriber.js
core.js
//core.js 部分代码
//实际上是继承了Subject类,是Angular的部分 Subject是Rxjs的部分
class EventEmitter extends Subject {
//定义了发射数据函数,并调用Subject的next方法
emit(value) { super.next(value); }
//定义了 订阅 方法,并调用了Subject 依赖的Observable 的 subscibe方法
subscribe(generatorOrNext, error, complete) {
//省略部分代码
const /** @type {?} */ sink = super.subscribe(schedulerFn, errorFn, completeFn);
//省略部分代码
}
}
core.js代码 是angular的代码,实际上就是定义了class EvemtEmitter 这个类,方便angluar中的使用,内部提供了emit与subscribe方法实现发布与订阅功能,实际上也是使用了rxjs的subject类
Subject.js
var Subject = /*@__PURE__*/ (function (_super) {
// Subject 继承 Observable
tslib_1.__extends(Subject, _super);
console.log(_super);
function Subject() {
var _this = _super.call(this) || this;
_this.observers = [];
_this.closed = false;
_this.isStopped = false;
_this.hasError = false;
_this.thrownError = null;
return _this;
}
Subject.prototype[rxSubscriberSymbol] = function () {
return new SubjectSubscriber(this);
};
Subject.prototype.lift = function (operator) {
var subject = new AnonymousSubject(this, this);
subject.operator = operator;
return subject;
};
//next 函数,将发布消息给observer
Subject.prototype.next = function (value) {
if (this.closed) {
throw new ObjectUnsubscribedError();
}
if (!this.isStopped) {
var observers = this.observers;
var len = observers.length;
var copy = observers.slice();
for (var i = 0; i < len; i++) {
copy[i].next(value);
}
}
};
//错误处理
Subject.prototype.error = function (err) {
if (this.closed) {
throw new ObjectUnsubscribedError();
}
this.hasError = true;
this.thrownError = err;
this.isStopped = true;
var observers = this.observers;
var len = observers.length;
var copy = observers.slice();
for (var i = 0; i < len; i++) {
copy[i].error(err);
}
this.observers.length = 0;
};
//手动完成
Subject.prototype.complete = function () {
if (this.closed) {
throw new ObjectUnsubscribedError();
}
this.isStopped = true;
var observers = this.observers;
var len = observers.length;
var copy = observers.slice();
for (var i = 0; i < len; i++) {
copy[i].complete();
}
this.observers.length = 0;
};
//取消订阅
Subject.prototype.unsubscribe = function () {
this.isStopped = true;
this.closed = true;
this.observers = null;
};
Subject.prototype._trySubscribe = function (subscriber) {
if (this.closed) {
throw new ObjectUnsubscribedError();
}
else {
return _super.prototype._trySubscribe.call(this, subscriber);
}
};
//在observable中调用_subscribe 将observer添加到this.Observable中
Subject.prototype._subscribe = function (subscriber) {
console.log(subscriber);
if (this.closed) {
throw new ObjectUnsubscribedError();
}
else if (this.hasError) {
subscriber.error(this.thrownError);
return Subscription.EMPTY;
}
else if (this.isStopped) {
subscriber.complete();
return Subscription.EMPTY;
}
else {
this.observers.push(subscriber);
return new SubjectSubscription(this, subscriber);
}
};
Subject.prototype.asObservable = function () {
var observable = new Observable();
observable.source = this;
return observable;
};
Subject.create = function (destination, source) {
return new AnonymousSubject(destination, source);
};
return Subject;
}(Observable));
export { Subject };
Subject.js中的subject类定义了 next、complete、error、_subscirbe等方法,实现订阅与发布
Subscriber.js:在Subject的Observers中存放的就是Subscriber 理解为订阅者。
Observable.js:提供了订阅方法。
toSubscriber.js:将返回Subscriber对象
- 整体的代码流程
1.test1组件中使用core.js的方法订阅了数据,并传入一个处理订阅数据的函数。
2.在core.js中订阅函数会调用Observable的订阅函数Subscribe,在这个函数中将传入的处理函数转成 subscirber
3.由于这种方式Observable的operator是空的,所有会回调到subject.js中的_subscribe()方法将subscriber放到subject的observers中
4.test2组件中某个定义是事件触发core.js中的emit方法
5.emit方法又调用了subject的next方法
6.调用subscriber中的next方法
7.调用subscirber中的_next方法
8.调用SafeSubscriber中的next方法(在subscriber的构造函数中会将destination转化成SafeSubscriber)
9.调用SafeSubscriber中的_tryOrUnsub方法,执行test1传进来的函数
- 总结:
关于RXJS的Subject还有很多扩展的地方,本文意在完成父子组件之间的数据传递。如有任何概念性错误望指正,谢谢!
Angular 个人深究(二)【发布与订阅】的更多相关文章
- redis 发布与订阅原理分析
前言:用了redis也有一段时间了,但是发布与订阅的使用频率也不高,趁着这次空闲,深究下redis的发布与订阅模式. 一.订阅频道和信息发布 功能说明:Redis 的 SUBSCRIBE 命令可以让客 ...
- Replication的犄角旮旯(二)--寻找订阅端丢失的记录
<Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...
- 知方可补不足~SQL2008中的发布与订阅模式
回到目录 作用:完成数据库与数据库的数据同步 原理:源数据库发布需要同时的表,存储过程,或者函数:目标数据库去订阅它,当源发生变化时,目标数据库自己同步,注意,由于这个过程是SQL自动完成的,所以要求 ...
- Redis - 发布和订阅
一.概述 1). 发布和订阅是一种消息通信模式. 2). 优点:使消息订阅者和消息发布者耦合度降低,类似设计模式中的观察者模式. 二.发布和订阅 订阅命令: // 订阅一个或多个频道 // 返回值:v ...
- SQL SERVER发布与订阅 [原创]
一.配置分发 1.配置分发服务器,注:配置发布与订阅,连接SQLSERVER必须用服务器名登录 2.配置分发 3.选择分发服务器 4.选择快照文件夹 5.设置此文件夹的读写权限为everyone 6. ...
- 文成小盆友python-num12 Redis发布与订阅补充,python操作rabbitMQ
本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ...
- Angular 个人深究(四)【生命周期钩子】
Angular 个人深究(四)[生命周期钩子] 定义: 每个组件都有一个被 Angular 管理的生命周期. Angular 创建它,渲染它,创建并渲染它的子组件,在它被绑定的属性发生变化时检查它,并 ...
- Angular 个人深究(三)【由Input&Output引起的】
Angular 个人深究(三)[由Input&Output引起的] 注:最近项目在做别的事情,angular学习停滞了 1.Angular 中 @Input与@Output的使用 //test ...
- SQL SERVER发布与订阅
一.配置分发 1.配置分发服务器,注:配置发布与订阅,连接SQLSERVER必须用服务器名登录 2.配置分发 3.选择分发服务器 4.选择快照文件夹 5.设置此文件夹的读写权限为everyone 6. ...
随机推荐
- composer设计原理与基本用法
原文地址:http://blog.turn.tw/?p=1039 COMPOSER進階原理:PHP命名空間與PSR-0 http://blog.turn.tw/?p=1122 Moving PHP ...
- shiroWeb项目-授权(十一)
使用PermissionsAuthorizationFilter 在applicationContext-shiro.xml中配置url所对应的权限. 测试流程: 1.在applicationCont ...
- ubuntu + usb转RS232驱动
1. 购买USB转串RS232/485/422 如果你的电脑有串口的话,就不用买啦,我的台式机有串口,把USB转串的线插上之后,unbuntu就不支持了.(自己有嘛) 就是输入 ls /dev/tt ...
- mysql 索引无法使用问题
今天碰到一个问题,表中有一个索引不使用,怎么强制也没用 ,force index都没用, 后来才发现是类型不对, 比如索引字段是int,如果参数使用varchar,那么是无法使用索引的,参数类型最好统 ...
- Centos7.5 防火墙设置
Centos7.5默认使用firewalld作为防火墙 1.查看firewalld服务状态 systemctl status firewalld 2.查看firewalld的状态 firewall-c ...
- k64 datasheet学习笔记3---Chip Configuration之System modules
1.前言 本文主要介绍芯片配置的系统模块的内容 2.SIM配置 TODO 3.SMC配置 TODO 4.PMC配置 TODO 5.LOW-LEAKAGE WAKEUP单元配置 TODO 6.MCM配置 ...
- [转]SPI通信原理简介
[转自]http://www.cnblogs.com/deng-tao/p/6004280.html 1.前言 SPI是串行外设接口(Serial Peripheral Interface)的缩写.是 ...
- Linux: 介绍make menuconfig中的每个选项含义【转】
转自:http://blog.csdn.net/gaoyuanlinkconcept/article/details/8810468 介绍make menuconfig中的每个选项含义 Linux 2 ...
- jdk8系列三、jdk8之stream原理及流创建、排序、转换等处理
一.为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX ...
- 红黑树与AVL树
概述:本文从排序二叉树作为引子,讲解了红黑树,最后把红黑树和AVL树做了一个比较全面的对比. 1 排序二叉树 排序二叉树是一种特殊结构的二叉树,可以非常方便地对树中所有节点进行排序和检索. 排序二叉树 ...