详解Angualr 组件间通信
发布时间 - 2026-01-10 22:41:12 点击率:次Angualr 组件间通信

约定: 遵循Angular官方的说法,下文中的AngularJS代指1.x版本,Angular代指Angular2及以后的升级版本。
采用Angular(或者任意MV*)的前端框架开发单页应用(SPA)时,我们都可能会遇见如下的场景:
A组件和B组件之前需要相互通信,或是A路由状态需要知道B路由状态的信息等等业务需求。
这个时候就需要设计到采用一套合理的通信方案来解决数据同步,数据通信的问题。
AngularJS 组件间的数据通信
在AngularJS中,也就是Angular JS 1.x版本中,我们需要实现控制器间的通信,有很多种方案,常见的有:
1. 采用 SharedService, 利用共享的公共服务来实现数据交换。
AngularJS中的Service被设计成单例的,这为这一方案,提供来底层的实现可行性,这一方案也是被广泛采用的。
2. 利用AngularJS提供的事件机制,$rootScope.$broadcast/ $scope.$emit 配合 $on 方法实现。
该方案的实现具备一定的局限性,比如:$emit方法只能向上传递事件,而不能实现向下的事件传播。但是进行合理的搭配组合已经基本够用了。
3. 利用浏览器的SessionStorage或者LocalStorage进行数据交换。
由于浏览器提供的这两类本地存储方案都提供了相应的storage事件,所以我们也可以使用该方案进行数据交换。使用该方案是应该注意及时清理无关数据。
4. 采用响应式的编程思想或者观察者模式的应用。关于这一类实现,需要经历一个编程思想的转变,之后会通过专门的文章进行记录。
5. 自身实现共享变量池。这个难度比较大,没有一定的设计能力并不推荐。
由于AngularJS并不是本文的重点,所以这里只简单的提一下。后面介绍的Angular的方案也有许多可以通用的地方。
Angular 组件间的数据通信
SharedService
共享服务方案在新的Angular中依然可以使用,而且无需额外的学习成本。这里在之前的学习笔记里有记录,不再纪录了。
SharedService 搭配 RxJS
听说 SharedService 和 RxJS 搭配更实用哦!这里的学习成本在于 RxJS ,RxJS只是 Rx思想的JS实现。这里强烈推荐学习Rx编程思想, 她的学习收益比绝对让你想象不到。
RxJS不需要我们不断的手动去SharedService中检查数据是否产生了变更,而是在数据有变化时,主动将变动的数据推送给感兴趣的任何订阅者。
举个栗子:
我们有一份随时可能会发生变动的数据存在了服务A中,在没有使用RxJS(或是类似框架/库)的情况下,我们想要知道数据的变化, 我们可能会采用轮询的机制去不断的询问服务A,我们关心的数据是否发生了变化,如果变化了就做出相应的动作。我们处于一种盲目的主动状态。
更高级一点的实现,就是我们可能把服务A实现称为一个可被观察的对象,这样我们便能通过观察服务A的状态来获取数据的变动。
现在我们来使用RxJS实现,RxJS现在可以理解为更高级的观察者模式实现。当使用来RxJS之后,在服务A中的数据发生变化时,服务A会主动 将变动的数据推送给每一个感兴趣的‘消费者',这些消费者处于被动接受数据,自主处理数据的状态中。RxJS不同于普通观察者模式的地方在于, 它提供来一系列的数据操作方法,比如:filter, map等等。这样就便于我们更加精细的处理数据。
下面通过一段简单的示例代码来感受一下:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class DataService {
private data: any;
private subject: Subject<any> = new Subject<any>();
setData(data: any): void {
this.data = data;
this.subject.next(data);
}
getData(): Observable<any> {
return this.subject.asObservable();
}
}
上面的代码中,我们简单的实现了一个可观察的数据服务,下面我们来使用这个服务
import { Component, OnInit } from '@angular/core';
import { DataService } from './shared/Dataservice';
@Component({
moduleId: module.id,
selector: '<my-component></my-component>',
templateUrl: '.name.component.html',
providers: [DataService]
})
export class MyComponent implements OnInit {
constructor(private dataService: DataService) {}
ngOnInit() {
// Will invoke data handler everytime
// when other component use the setter this.dataService.setData()
this.dataService.getData().subscribe(data => console.log(data));
}
}
使用Angular底层提供的 @Input 和 @Output 装饰器来实现组件间的通信服务
新的Angular采用Web Component的方式进行组件的封装,并将组件间的组织关系设计称为树状结构。这为应用数据的流向,管理提供了良好的支持, 也使得我们可以在Angular应用中使用一些别的库,比如: Redux 思想。基于这些设计理念,Angular为指令提供了更为强大的功能,组件也是指令。
采用 @Input 修饰属性,实现 parent -> child 组件间的通信
下面的示例代码将展示如何设置一个组件的Input属性
import { Component, Input, OnInit } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'child-component',
template: `I'm {{ name }}`
})
export class ChildComponent implements OnInit {
@Input() name: string;
constructor() { }
ngOnInit() { }
}
上面的代码中,我们实现了一个名叫ChildComponent的组件,这个组件的有一个采用@Input装饰器修饰的属性:name。
下面我们将展示如何使用这个这个组件,并为这个Input属性赋值。
import { Component, OnInit } from '@angular/core';
import { ChildComponent } from './child-component';
@Component({
moduleId: module.id,
selector: 'parent-component',
template: `<child-component [name]="childName"></child-component>`,
// This is unnecessary when installing ChildComponent within Root NgModule
directives: [ChildComponent]
})
export class ParentComponent implements OnInit {
private childName: string;
constructor() { }
ngOnInit() {
this.childName = 'StevenShen';
}
}
上面的代码实现中,在父组件中,我们为子组件的Input属性设置了父组件中的特定值。关键点在如下片段:
<child-component [name]="childName"></child-component>
Angular在进行AOT操作时,会将特定的值注入给ChildComponent中。
如果你在CodePen,或是自己的本地实验上面的代码你会发现,和AngularJS的指令中采用'@', ‘=', ‘&'等修饰的属性不一样的地方。
当父组件中的childName发生变化时,ChildComponent中的name属性并没有感知到变化。这是怎么了,是不是感觉新版的Angular在 和我们开玩笑,wtf!!!内心的表情是这样的 ○| ̄|_ 。(感觉一篇学习笔记开始被写的画风突变了。。。)
将父组件的属性变化映射到子组件中
上一小节的实现,虽然在初始化子组件时,我们可以将父组件的值反馈到子组件中。但是,初始化完成后,父组件中相关属性的变化却不能被子组件感知。
这无疑是让我们内心崩溃的。为什么和AngularJS不一样了???别急,下面我们将来提供解决方案。
利用Angular提供的组件生命周期钩子函数ngOnChanges来监听输入属性值的变化
需要实现让子组件感知到父组件中相关属性的变化,我们需要对Angualr组件的生命周期有一定的了解,采用Angular提供的组件生命周期的钩子函数, 进行组件间数据的同步。(关于Angualr组件的生命周期,之后会有相关的学习笔记整理。到时候在加上链接。)这里直接上代码:
import { Component, Input, SimpleChanges } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'child-component',
template: `I'm {{ name }}`
})
export class ChildComponent {
@Input() name: string;
ngOnChanges(changes: SimpleChanges) {
this.name = changes['childName'].currentValue;
}
}
采用ES5中的getter和setter方法进行输入属性的监听
在ES5中,我们在定义一个对象的属性时,可以通过Object.defineProperty方法为一个对象的属性设置关联的getter和setter方法, 当我们进行这一操作后,以后该对象上的相关属性的读取和赋值操作,都会调用相应的getter/setter方法进行预处理或改变操作结果。
同样的道理,在Angular中,我们通过设置和使用一个输入属性的setter方法,便可以拦截到父组件中相关值的变化,并进行特定的数据处理。
这种方法比较直观,直接上代码:
父组件的代码实现:
import { Component } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'name-parent',
template: `
<h2>Master controls {{names.length}} names</h2>
<name-child *ngFor="let name of names" [name]="name"></name-child>
`
})
export class ParentComponent {
names = ['StevenShen', ' ', ' lei '];
}
子组件的代码实现
import { Component, Input } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'name-child',
template: `<h3>"{{name}}"</h3>`
})
export class ChildComponent {
name: string = 'default name';
@Input()
set name(name: string) {
this.name = (name && name.trim()) || 'default name';
}
get name() { return this.name; }
}
采用 @Output 修饰属性,实现 child -> parent 组件间的通信
新版的 Angular 中,子组件和父组件间的通信,采用来事件的机制。这样的设计有助于组件的复用和代码的解耦;
我们不需要过多的关心组件的具体实现,我们只需要知道一个组件它接收哪些数据,并产生哪些输出事件即可。
直接上代码直观了解一下:
@Component({
moduleId: module.id,
selector: 'child-component',
template: `I'm {{ name }}`
})
export class ChildComponent {
@Input() name: string;
@Output() say: EventEmitter<boolean> = new EventEmitter<boolean>();
ngOnChanges(changes: SimpleChange) {
this.name = changes['childName'].currentValue;
}
speak() {
this.say.emit(true);
}
}
子组件变更完成后,我们来变更父组件的代码实现。
import { Component, OnInit } from '@angular/core';
import { ChildComponent } from './child-component';
@Component({
moduleId: module.id,
selector: 'parent-component',
template: `<child-component [name]="childName" (say)="isChildSpeak($event)"></child-component>`,
// This is unnecessary when installing ChildComponent within Root NgModule
directives: [ChildComponent]
})
export class ParentComponent implements OnInit {
private childName: string;
constructor() { }
ngOnInit() {
this.childName = 'StevenShen';
}
isChildSpeak(isIt: boolean) {
console.log('The child speak status is %s', isIt ? 'ture' : 'false');
}
}
这样一来就实现了父子组件间的通信了。
但是这样的实现存在一定的局限性:父组件不能使用数据绑定来读取子组件的属性或调用子组件的方法.
通过 @ViewChild 获取组件的控制器/模版进行组件间的通信
除开使用 @Input 和 @Output 修饰器搭配Angular的生命周期钩子函数进行组件间通信。 我们还可以采用@ViewChild来进行不同组件间的通信,而不仅仅局限于父子组件间的通信。同时,采用@ViewChild的方式, 我们可以获得更为精细的组件控制权限,比如在父组件中读取子组件的属性值或调用子组件的方法。我们依然采用上面的代码来进行改造。
对于ChildComponent组件的变更:
import { Component } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'child-component',
template: `I'm {{ name }}`
})
export class ChildComponent {
public name: string;
speak() {
console.log('say something whitout EventEmitter');
}
}
对于ParentComponent组件的变更:
import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
import { ChildComponent } from './child-component.ts';
@Component({
moduleId: module.id,
selector: 'parent-component',
// attention #childCmp tag
template: `
<child-component #childCmp></child-component>
<button (click)="child.name = childName"></button>
`,
// This is unnecessary when installing ChildComponent within Root NgModule
directives: [ ChildComponent ]
})
export class ParentComponent implements OnInit, AfterViewInit {
@ViewChild('childCmp') childCmp: ElementRef;
constructor() { }
ngOnInit() {
this.childCmp.name = 'StevenShen';
}
ngAfterViewInit() {
this.childCmp.speak();
}
}
通过上面的代码改造,我们同样可以实现不同组件间的通信,而且这样的组件通信已经不仅仅局限于父子组件间的通信了。
总结
由于技术水平和时间原因,这篇文章完成得比较粗略。主要整理的都是自己在工作中实际使用到的一些方案。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Angualr
# 组件间通信
# angular2
# angularjs
# 组件通信
# AngualrJS中的Directive制作一个菜单
# AngualrJS中每次$http请求时的一个遮罩层Directive
# 这一
# 数据通信
# 不需要
# 数据交换
# 学习笔记
# 有一定
# 感兴趣
# 我们可以
# 实现了
# 可以使用
# 来实现
# 使用这个
# 局限于
# 这为
# 信了
# 自己的
# 都是
# 这是
# 是在
# 也有
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制
Laravel如何自定义错误页面(404, 500)?(代码示例)
Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】
bootstrap日历插件datetimepicker使用方法
Linux系统命令中screen命令详解
如何在宝塔面板中修改默认建站目录?
Laravel怎么实现模型属性的自动加密
javascript中的数组方法有哪些_如何利用数组方法简化数据处理
Linux网络带宽限制_tc配置实践解析【教程】
Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程
Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践
如何用wdcp快速搭建高效网站?
如何快速配置高效服务器建站软件?
移动端手机网站制作软件,掌上时代,移动端网站的谷歌SEO该如何做?
千问怎样用提示词获取健康建议_千问健康类提示词注意事项【指南】
Laravel怎么配置.env环境变量_Laravel生产环境敏感数据保护与读取【方法】
Laravel如何发送系统通知_Laravel Notifications实现多渠道消息通知
Swift中循环语句中的转移语句 break 和 continue
HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】
Laravel怎么自定义错误页面_Laravel修改404和500页面模板
香港网站服务器数量如何影响SEO优化效果?
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解
如何在云主机上快速搭建多站点网站?
高防服务器:AI智能防御DDoS攻击与数据安全保障
Android使用GridView实现日历的简单功能
Laravel如何为API生成Swagger或OpenAPI文档
如何在万网主机上快速搭建网站?
常州企业网站制作公司,全国继续教育网怎么登录?
Laravel如何发送邮件_Laravel Mailables构建与发送邮件的简明教程
html5audio标签播放结束怎么触发事件_onended回调方法【教程】
php json中文编码为null的解决办法
如何在Windows虚拟主机上快速搭建网站?
Laravel如何创建自定义中间件?(Middleware代码示例)
长沙做网站要多少钱,长沙国安网络怎么样?
HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】
电商网站制作价格怎么算,网上拍卖流程以及规则?
Microsoft Edge如何解决网页加载问题 Edge浏览器加载问题修复
EditPlus 正则表达式 实战(3)
如何在IIS7上新建站点并设置安全权限?
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
EditPlus中的正则表达式实战(5)
JavaScript如何实现继承_有哪些常用方法
Laravel观察者模式如何使用_Laravel Model Observer配置
html5的keygen标签为什么废弃_替代方案说明【解答】
作用域操作符会触发自动加载吗_php类自动加载机制与::调用【教程】
怎么用AI帮你设计一套个性化的手机App图标?
Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】
edge浏览器无法安装扩展 edge浏览器插件安装失败【解决方法】
ChatGPT 4.0官网入口地址 ChatGPT在线体验官网
Laravel如何处理CORS跨域问题_Laravel项目CORS配置与解决方案

