angualr8观察者模式_可视化的 Angular 响应式编程

本文探讨了在现代化UI开发中,Angular如何处理视图与模型的双向绑定,以及响应式编程的重要性。Angular的响应式特性通过Angular的AsyncPipe和RxJS实现了数据的自动同步,但同时也带来了一些问题,如代码分散、初始状态处理和副作用处理等。文章提出了一种新的Reactive State模式,旨在提高组件的响应式编程可维护性,通过中心化的状态处理方法和外部工具,实现更高效、更简洁的状态管理和响应式组件设计。
摘要由CSDN通过智能技术生成

现代化UI开发中,客户端(前端)一般会进行分层设计,实际用户可感知的 UI 作为顶层,称为视图(View),底层中独立于展示方式的数据结构称为模型(Model),而将两者进行关联的中间层部分,根据划分方式则有很多种定义,例如控制器(Controller)、展示器(Presenter)以及视图模型(ViewModel)等。

程序设计中,UI相关的平台API一般以面向对象为基础,通过属性(Property)或者方法(Method)暴露相关状态及行为,供开发人员使用。UI 编程的本质是花式建立双向绑定,例如对于 元素,其 value 属性往往需要同时被程序读取和写入,同时需要满足读取的结果会作用于下次写入,并且写入的结果会作用于下次读取。如果不满足双向绑定的约束关系,要么会产生数据丢失、要么用户操作会被打断。几乎任何 DOM 属性都是双向绑定属性,甚至包括 innerHTML,不过由于其不会因用户交互发生预期的状态变化,因此往往不会进行监听和读取。

响应式概览

UI本质上是对应用状态的体现,因此一旦UI状态失去同步将很容易造成不预期的结果,因此UI响应式编程的重要性不言而喻。响应式编程(Reactive Programming),顾名思义,指通过声明式的设计指定数据依赖关系,并且仅以最小成本应对外界依赖的变化进行相应的数据处理。一个不是十分恰当的简单版本是,非响应式编程是基于拉取(Pull),而响应式编程是基于推送(Push)。

对于视图状态的双向绑定,UI开发框架通常能够在很大程度上进行简化,自动或半自动地将视图状态与某个类实例的属性、某个 State 对象的属性、某个 Store 对象的属性进行同步,保持状态间的关联。但是,只有这层同步关系远远满足不了应用的需求,开发人员的最终目标可能是将状态同步到本地的持久存储空间,甚至跨越互联网同步到服务器。而由于成本限制和用户体验要求,实时同步往往并不可行,只能靠多级缓存的方式达到间接同步,从而让应用的状态管理复杂化。

Angular 概览

Angular是一个 UI 开发框架,采用准 MVVM 的层次划分方式,组件模板对应于视图,而组件类对应于视图模型。Angular 本身很出色地处理了 View 与 ViewModel 之间的绑定关系,只需要修改组件实例的属性,模版中的绑定结果就会自动应用于视图,一个简单组件为:

@Component({

template: `Hello { { name | uppercase }}!`,

})

class HelloComponent {

name = 'World'

}

// Results to "Hello WORLD!"

这里通过模板表达式建立了 DOM(View)与组件类(ViewModel)的绑定,随着 ViewModel 的变化,View 也会自动应用相应的更新。

虽然看上去如此美好,不过真正的开发成本并不在于 View 与 ViewModel 的同步,而是存在于 Model 与 ViewModel 之间同步,即如何根据一系列的外部状态确定 name 属性的值并将其更新。如果处理不妥,可能会导致代码量爆炸并且使得数据流向混乱,类似于:

class HelloComponent {

@Input() name = 'World'

ngOnInit() {

someExternalCallbackFn((name) => {

this.name = name

})

}

onSomeEvent(name: string) {

this.name = name

}

}

随着时间的推移和人员的变动,如果没有优秀的代码组织方案,项目代码往往都会向着混沌的方向发展,造成可读性与可维护性地持续恶化。

Angular 与 RxJS

Reactive Extensions 是微软提出的响应式设计方案,除了自身的 .Net 版本外,目前已有多个不同语言的社区实现。Rx 是

提供方返回 Observable 对象,消费者订阅该对象,即可实现对数据更新的持续可知状态。例如服务能够通过 Observable 暴露持续产生的数据:

@Injectable()

class MyService {

myProperty: Observable

myMethod(foo: boolean): Observable {}

}

为了获得一个 Observable 提供的数据(也可能是不包含数据纯事件通知),需要对其进行订阅,从而在回调中得到数据内容:

class MyComponent {

value: number

constructor(private myService: MyService) {}

ngOnInit() {

myService.myMethod(true).subscribe(value => {

this.value = value

})

}

}

而为了让数据流向更加清晰,能够将多个 Observable 进行组合,对消费者隔离实现细节,但仍然对静态分析友好,使用「Go to definition」即可确定完整的数据流向。类似于:

class MyService {

myMethod(foo: boolean): Observable {

const source3$ = getDynamicSource(foo)

return someComposeFn(

source1$,

source2$,

).pipe(

someOperator(source3$),

)

}

}

其中 source1$、source2$ 与 source3$ 本身也可能是组合的结果,Observable 的可组合性一定程度上保证了分层设计中的隔离性,避免将数据聚合集中到最终消费者的位置。

不过,Observable 的使用本身仍然需要大量样板代码,对开发者而言是一个负担,例如 Angular 中可能出现的场景是:

@Component({

template: `

Value: { { value }}

Count: { { count }}

`,

})

class MyComponent {

@Input() clicks: Observable;

value: number = 0

count: number = 1</

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值