当前位置: 代码网 > it编程>前端脚本>AngularJs > Angular组件间通信的新解决方案详解

Angular组件间通信的新解决方案详解

2024年05月18日 AngularJs 我要评论
引言其中首创了公共的dataservice,用于任意组件间通信。 dataservice通过angular service特性和注册表的使用,可以实现消息的一收一发,无需再写单独的service逻辑。

引言

其中首创了公共的dataservice,用于任意组件间通信。 dataservice通过angular service特性和注册表的使用,可以实现消息的一收一发,无需再写单独的service逻辑。

背景

一般来讲,angular 已有的组件间通信方式有哪些?

序号通信方式描述
1输入属性(@input)通过属性绑定,将数据从父组件传递给子组件。
2输出属性(@output)通过事件绑定,子组件可以发送事件给父组件,并传递数据。
3父子组件直接访问在某些情况下,父组件可以通过 viewchild 或 contentchild 装饰器直接访问子组件或模板中的元素。
4服务(service)创建共享的服务,组件可以注入该服务来存储和获取数据。
5rxjs subject 和 observable使用 rxjs 中的 subject 和 observable 来实现组件之间的消息传递。
6angular 路由参数通过路由参数在不同组件之间传递数据。
7ngrx使用 ngrx 状态管理库来实现更复杂的组件间通信和数据共享。

但以上方法各有局限性,要么代码繁多,要么学习成本高,尤其是跨越多个组件的通信,例如下图中从组件d组件g,用前两种方式就太繁琐了,方式 4 和 5 可能是常用解决方案,但是仍然有些繁琐——需要为每一组消息传递写专有的 service 代码

组件a
       /      \
     组件b      组件c
    /   \      /    \
  组件d 组件e 组件f  组件g

本文介绍的通信方式是在方式 4 和 5 的基础上,进行了做了特殊的抽象处理,实现了一个公共的 dataservice,最终实现了消息一收一发,无需再写中间环节的代码。

常规方式实现组件间消息通信

为了做对比,这里先介绍一下前文所示的方式4+5方案,这个方案通过 service 和 rxjs 的 subject 结合使用,实现任意组件间通信。

首先,我们创建一个名为messageservice的服务,用于在组件之间传递消息:

// message.service.ts
import { injectable } from '@angular/core';
import { subject } from 'rxjs';
@injectable({
  providedin: 'root',
})
export class messageservice {
  private messagesubject = new subject<string>();
  // observable string stream
  message$ = this.messagesubject.asobservable();
  // service method to send a message
  sendmessage(message: string) {
    this.messagesubject.next(message);
  }
}

接下来,我们有两个组件,sendercomponentreceivercomponentsendercomponent用于发送消息,而receivercomponent用于接收消息。

// sender.component.ts
import { component } from '@angular/core';
import { messageservice } from './message.service';
@component({
  selector: 'app-sender',
  template: `
    <input type="text" [(ngmodel)]="message" />
    <button (click)="sendmessage()">发送消息</button>
  `,
})
export class sendercomponent {
  message: string;
  constructor(private messageservice: messageservice) {}
  sendmessage() {
    this.messageservice.sendmessage(this.message);
    this.message = ''; // 清空输入框
  }
}
// receiver.component.ts
import { component } from '@angular/core';
import { messageservice } from './message.service';
@component({
  selector: 'app-receiver',
  template: ` <div>接收到的消息: {{ receivedmessage }}</div> `,
})
export class receivercomponent {
  receivedmessage: string;
  constructor(private messageservice: messageservice) {
    this.messageservice.message$.subscribe(message => {
      this.receivedmessage = message;
    });
  }
}

在这个示例中,我们通过messageservice来实现了sendercomponentreceivercomponent发送消息的功能。messageservice中使用了 rxjs 的subject来创建一个可观察的消息流,然后在sendercomponent中调用sendmessage方法来发送消息,而在receivercomponent中使用subscribe来订阅消息流并接收消息。

请注意,为了使messageservice成为全局可用的单例服务,我们在@injectable装饰器中设置了providedin: 'root'。这样一来,messageservice将成为整个应用程序中所有组件共享的单一实例。

为了使示例正常工作,别忘了将sendercomponentreceivercomponent添加到所属的模块中,并在模块的模板中放置对应的组件选择器。

这样,sendercomponent发送的消息将通过messageservice传递给receivercomponent,并显示在receivercomponent中。这就完成了通过 service 和 rxjs 的 subject 实现组件间消息通信的示例。

新的解决方案

理解本方案,默认需要熟悉 angular 的 service 存储传递数据原理 和 rxjs 的多播用法。

本方案原理是,通过 service 单例的特性(service 在模块内组件间是共享的)和 subject 的多播特性,实现一个公共的 service,通过公共的 service 实现数据传递。相对的,如同前文中的message.service.ts文件所示,开发者需要为每一组通信单独创建 service 文件,单独写响应的逻辑。

理解本方案的三个关键点:

单例 通过在@injectable装饰器中设置providedin: 'root',service 成为整个应用程序中所有组件共享的单一实例。因为是共享的,所以能作为通信中消息的载体。这是本方案的根本前提。

rxjs 的多播 基础原理是观察者模式(即发布订阅模式)

注册表 为了复用 service,简化代码,本方案引入了一个注册表,用来存储对应每个消息事件的 subject 对象。subject 对象在创建监听时(需要接收消息的地方)创建。

service 代码如下:

// dataservice.service.ts
import { injectable } from '@angular/core';
import { subject, subscription } from 'rxjs';
@injectable({
  providedin: 'root',
})
export class dataservice<t> {
  // 创建注册表,用于存放监听器
  private events = new map();
  /**
   * 发送消息
   *
   * @param {string} event 事件。用于区别不同的监听
   * @param {t} value 消息内容
   * @returns {void}
   * @memberof dataservice
   */
  sendmessage(event: string, value: t): void {
    if (!this.events.has(event)) {
      return;
    }
    this.events.get(event).subject.next(value);
  }
  /**
   * 获取监听器
   *
   * 监听器其实就是一个rxjs subject对象,通过订阅来获取数据。
   *
   * 注意:
   * 1.getlistener()应该在sendmessage()之前,否则sendmessage()中获取不到监听器,无法发消息
   * 2.getlistener()应放在ngoninit()、ngafterviewinit()等只会执行一次的生命周期函数中
   *
   * @param {string} event 事件。用于区别不同的监听
   * @returns {subject<t>}
   * @memberof dataservice
   */
  getlistener(event: string): subject<t> {
    // 多处监听时会走到此分支
    if (this.events.has(event)) {
      const current = this.events.get(event);
      current.count++;
      return current.subject;
    }
    const listener = {
      count: 1, // 该字段用于记录监听(订阅)者个数
      subject: new subject<t>(),
    };
    /**
     * 创建监听器,并将其加入注册表
     *
     * 所在函数在创建监听(订阅)时调用,监听发生在发送消息之前,所以在监听这里将监听器加入注册表
     */
    this.events.set(event, listener);
    return listener.subject;
  }
  /**
   * 取消订阅
   *
   * 必须手动取消订阅。
   * 取消时检查监听者个数。如果没有监听者了,就移除监听器。
   *
   * @param {string} event
   * @param {subscription} subscription
   * @returns
   * @memberof dataservice
   */
  cancelsubscription(event: string, subscription: subscription) {
    if (!this.events.has(event)) {
      return;
    }
    const current = this.events.get(event);
    current.count--;
    if (current.count === 0) {
      // 没有监听者了,就移除监听器
      this.events.delete(event);
    }
    subscription.unsubscribe();
  }
}

使用时只需引入上面的公共 dataservice,然后以如下示例的方式直接调用 api 就行。

创建监听的示例:

// 接收传递过来的消息
  receivechangemessage;
  ngoninit() {
    ...
    this.subscription = this.dataservice.getlistener('event_name').subscribe(message => {
        this.receivechangemessage = message;
      });
    ...
  }
  ngondestroy() {
    this.dataservice.cancelsubscription('event_name', this.subscription);
  }

发送消息的示例:

public onsomethingchange(value: boolean): void {

    ...

    this.dataservice.sendmessage('event_name', value);
  }

给出一个较完整的使用示例

以下是一个使用上文提供的dataservice实现组件间消息通信的示例:

假设我们有两个组件:sendercomponentreceivercomponent,它们需要通过dataservice来传递消息。

// sender.component.ts
import { component } from '@angular/core';
import { dataservice } from './data.service';
@component({
  selector: 'app-sender',
  template: `
    <input type="text" [(ngmodel)]="message" />
    <button (click)="sendmessage()">发送消息</button>
  `,
})
export class sendercomponent {
  message: string;
  constructor(private dataservice: dataservice<string>) {}
  sendmessage() {
    this.dataservice.sendmessage('customevent', this.message);
    this.message = ''; // 清空输入框
  }
}
// receiver.component.ts
import { component, oninit, ondestroy } from '@angular/core';
import { dataservice } from './data.service';
import { subscription } from 'rxjs';
@component({
  selector: 'app-receiver',
  template: ` <div>接收到的消息: {{ receivedmessage }}</div> `,
})
export class receivercomponent implements oninit, ondestroy {
  receivedmessage: string;
  subscription: subscription;
  constructor(private dataservice: dataservice<string>) {}
  ngoninit() {
    this.subscription = this.dataservice.getlistener('customevent').subscribe(message => {
      this.receivedmessage = message;
    });
  }
  ngondestroy() {
    this.dataservice.cancelsubscription('customevent', this.subscription);
  }
}

在这个示例中,我们通过dataservice实现了sendercomponentreceivercomponent发送消息的功能。dataservicesendmessage方法用于发送消息,而getlistener方法用于订阅消息,并在接收到消息时更新receivercomponent中的receivedmessage属性。cancelsubscription方法用于取消订阅,并在不再有监听者时从注册表中移除监听器。

请确保将sendercomponentreceivercomponent添加到所属的模块中,并在模块的模板中放置对应的组件选择器。

通过使用dataservicesendercomponent发送的消息将传递给receivercomponent,并显示在receivercomponent中。这样,我们成功地实现了组件间消息通信。

实践拓展

后续可以做成插件,以装饰器的形式调用。

export class receivecomponent {
  @listen('messageevent')
  message;
}

export class sendcomponent {
  @send('messageevent')
  message;
}

以上就是 屈金雄 同学的分享,如果你也有更多前端技术想与我们交流,欢迎投稿。除此之外,也欢迎你参与到 opentiny 开源中来👏,一起共建项目,一起研讨前端技术。

关于 opentiny

opentiny 是一套企业级组件库解决方案,适配 pc 端 / 移动端等多端,涵盖 vue2 / vue3 / angular 多技术栈,拥有主题配置系统 / 中后台模板 / cli 命令行等效率提升工具,可帮助开发者高效开发 web 应用。

核心亮点:

  • 跨端跨框架:使用 renderless 无渲染组件设计架构,实现了一套代码同时支持 vue2 / vue3,pc / mobile 端,并支持函数级别的逻辑定制和全模板替换,灵活性好、二次开发能力强。
  • 组件丰富:pc 端有80+组件,移动端有30+组件,包含高频组件 table、tree、select 等,内置虚拟滚动,保证大数据场景下的流畅体验,除了业界常见组件之外,我们还提供了一些独有的特色组件,如:split 面板分割器、ipaddress ip地址输入框、calendar 日历、crop 图片裁切等
  • 配置式组件:组件支持模板式和配置式两种使用方式,适合低代码平台,目前团队已经将 opentiny 集成到内部的低代码平台,针对低码平台做了大量优化
  • 周边生态齐全:提供了基于 angular + typescript 的 tinyng 组件库,提供包含 10+ 实用功能、20+ 典型页面的 tinypro 中后台模板,提供覆盖前端开发全流程的 tinycli 工程化工具,提供强大的在线主题配置平台 tinytheme

以上就是angular组件间通信的新解决方案详解的详细内容,更多关于angular组件间通信的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com