Angular服务直接共享数据对象是否合理?相较Observable模式有何弊端?
这种直接共享服务可变对象的模式存在以下几个核心缺陷:
硬伤:Angular 服务不支持
ngOnInit生命周期钩子
你写在UserService里的ngOnInit方法永远不会被自动调用,只有组件和指令类才会触发Angular的生命周期钩子,服务的初始化逻辑必须写在构造函数中。这也是你贴的示例代码本身就无法正常运行的核心问题,初始化获取用户信息的请求根本不会发起。OnPush 变更检测下直接失效
现在中大型Angular项目几乎都会给组件开启ChangeDetectionStrategy.OnPush来优化性能,该策略下只有当组件的输入属性引用变化、组件内部触发事件、或者手动触发变更检测时,才会更新视图。你直接修改user对象的属性时,对象引用没有发生变化,所有开了OnPush的组件都不会自动更新视图,最终只能靠手动调用detectChanges强制更新,反而增加了冗余代码,还容易出现视图不同步的BUG。异步时序无显性管控,易出空值错误
你将user初始化为空的User实例,在接口请求返回前,所有绑定userService.user.name的模板要么渲染空值,要么如果User类没有默认给name赋初始值,还会直接抛出属性读取错误。如果要加加载态、空态判断,你需要在每个用到的组件里单独写判断逻辑,而用async管道配合*ngIf="user$ | async as user"的写法,可以统一处理加载态,接口返回前整个区块都不会渲染,从语法层面避免了空值错误。状态突变不可控,维护成本极高
你把user对象设为public,任何注入了UserService的组件都可以直接修改user的任意属性,不需要经过服务暴露的方法,也没有统一的校验、日志、拦截逻辑。项目小的时候还好,一旦项目变大、多人协作,哪天用户数据出现异常,你根本没法排查是哪个组件偷偷修改了属性,状态会彻底失控。错误处理、扩展能力极弱
你示例中的HTTP请求没有任何错误处理逻辑,一旦获取用户信息的接口请求失败,user永远是空实例,你没法统一做报错提示、跳转登录页、重试请求等逻辑。后续如果要做用户状态变更的监听、防抖节流、和其他数据流联动等需求,你还要额外加Subject封装,反而比一开始就用BehaviorSubject+shareReplay的响应式写法多了很多冗余代码。
之所以官方和社区很少采用这种实现,是因为它只适合极小的Demo场景,只要项目复杂度稍微上升,它带来的维护成本会远高于少写的那几行代码,而响应式数据流的写法经过大量项目验证,长期的可维护性、扩展性、稳定性都高得多。
内容的提问来源于stack exchange,提问作者Niels van Rijn

