资讯专栏INFORMATION COLUMN

[译] 你真的知道 Angular 单向数据流吗

fox_soyoung / 965人阅读

摘要:所以,单向数据流的意思是指在变更检测期间属性绑定变更的架构。相反,输出绑定过程并没有在变更检测期间内运行,所以它没有把单向数据流转变为双向数据流。说的单向数据流说的是服务层,而不是视图层嗷。

原文链接: Do you really know what unidirectional data flow means in Angular
关于单向数据流,还可以参考这篇文章,且文中还有 youtube 视频解析:Angular - What is Unidirectional Data Flow? Learn How the Angular Development Mode Works, why it"s important to use it and how to Troubleshoot it 。单向数据流一句话解释就是:不要在 Angular 使用 Model 生成 View 这个过程中再去修改 Model。

大多数架构模式是很难理解的,尤其是在相关资料很少时那就更加头疼,比如 Angular 的单向数据流(unidirectional data flow)文档资料就很少,即使官方文档上,也仅仅在 表达式指南模板表达式 两小块中略有提及。我也很少在网上搜到比较好的解释文章,所以写此文是为了给 单向数据流(unidirectional data flow) 有个详细的解释。

双向数据流和单向数据流

在讨论 AngularJS 和 Angular 性能异同点时,就经常会提到单向数据流模式,它也是 Angular 比 AngularJS 性能更快的原因所在。让我们一起看看单向数据流究竟咋回事吧!(译者注:AngularJS 指的是 1.X 版本,2+ 版本叫 Angular,使用 TypeScript 语言重写了框架,包括架构模式进行了重新设计,但两者形似也神似。)

AngularJS 和 Angular 都是通过绑定来实现组件间数据通信,比如在 AngularJS 定义一个父组件 A

app.component("aComponent", {
  controller: class ParentComponent() {
    this.value = {name: "initial"};
  },
  template: `
    
  `
});
----------------
app.component("bComponent", {
    bindings: {
        obj: "="
    },

可以看到父组件 A 有个子组件 B,并且通过输入绑定把父组件 Avalue 属性值传给子组件 Bobj 属性:

这和 Angular 中组件间通信很类似了啊:

@Component({
    template: `
        
    ...
export class AppComponent {
    value = {name: "initial"};
}
----------------
export class BComponent {
    @Input() obj;

第一件重要的事情是,Angular 和 AngularJS 都是在变更检测(change detection)期间才去更新绑定值。所以,当 Angular 框架在为父组件 A 运行变更检测时,它会更新子组件 Bobj 属性:

bComponentInstance.obj = aComponentInstance.value;

上面过程证明了单向数据流是数据从上往下流(译者注:即数据从组件树的父组件往子组件流),但是 AngularJS 却不一样,它可以允许在子组件中更新父组件的 value 属性值

app.component("parentComponent", {
  controller: function ParentComponent($timeout) {    
    $timeout(()=>{
      console.log(this.value); // logs {name: "updated"}
    }, 3000)
  }
----------------
  
app.component("childComponent", {
    controller: function ChildComponent($timeout) {      
      $timeout(()=>{
        this.obj = { name: "updated" };  
      }, 2000)

上面代码你可以看到两个 timeout 回调,第一个回调会更新子组件属性,第二个回调会延迟第一个回调一秒,检查父组件属性是否被更新。如果你在 AngularJS 中运行上面代码你会发现父组件属性已经被更新了。让我们一起看看究竟发生了什么?

当第一个回调运行时,子组件 Bobj 属性被更新为 {name: "updated"},然后 AngularJS 运行变更检测。在变更检测过程中,AngularJS 检测到子组件绑定属性的值发生改变,它会更新父组件 Avalue 属性值。这是 AngularJS 变更检测的内置功能。如果你在 Angular 中做同样的事情,它仅仅更新子组件的属性值,但是子组件的改变不会冒泡到父组件,即 Angular 不会改变父组件的属性值。这是升级版后的 Angular 变更检测实现,相较于 AngularJS 的最重大区别。然而,它却困扰了我好久啊。

然而,Angular 也同样可以通过子组件来更新父组件,这个机制就是输出绑定(output binding)。可以像这样使用输出绑定:

@Component({
    template: `
        

Hello {{value.name}}

... export class AppComponent { value = {name: "initial"}; constructor() { setTimeout(() => { console.log(this.value); // logs {name: "updated"} }, 3000); ---------------- @Component({...}) export class AComponent { @Output() updateObj = new EventEmitter(); constructor() { setTimeout(() => { this.updateObj.emit({name: "updated"}); }, 2000);

我承认这个和从子组件直接更新还不太一样,但是父组件值的确是更新了啊。很长一段时间我不明白为何这没有被看做双向数据绑定?毕竟,数据通信是在两个方向进行的。

直到有一晚我读到了 Two Phases of Angular Applications by Victor Savkin,他解释道(译者注:为清晰理解,这个解释不翻译):

Angular 2 separates updating the application model and reflecting the state of the model in the view into two distinct phases. The developer is responsible for updating the application model. Angular, by means of change detection, is responsible for reflecting the state of the model in the view.
(译者注:意思就是 Angular 划分了更新程序 model在前同步 model 和 view 在后两个步骤,开发者只需要关注更新程序 model同步 model 和 view 由 Angular 框架负责,这个同步过程就是变更检测,说白了就是更新 view。)
(译者注:比如朋友圈点赞,首先就是手指触发异步事件来更新程序 model,如在 Post 组件里给 likes 属性加 1,然后在下一个 VM turn 时 Angular 会自动更新视图中绑定的 likes 值, 即同步 model 和 view,点赞数就会从 0 变为 1。)

这让我花费好些天才明白,所谓的使用输出绑定机制来更新父组件并不是在变更检测中执行的:

相反,它是在变更检测前的更新程序 model 阶段执行的。所以,单向数据流的意思是指在变更检测期间属性绑定变更的架构。在上面 Angular 代码示例中,不像 AngularJS,在 Angular 变更检测期间,并没有代码去让子组件 AComponent 去更新父组件 AppComponent 的属性。相反,输出绑定过程并没有在变更检测期间内运行,所以它没有把单向数据流转变为双向数据流。

另外,尽管 Angular 没有内置机制可以使得在变更检测期间去更新父组件,然而可以通过共享服务或广播同步事件做到这一点。但是,由于 Angular 框架强迫单向数据流,所以这么做会导致ExpressionChangedAfterItHasBeenCheckedError 错误。想要了解导致这个错误的原因和解决方案可以参考 Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError

视图和服务层的单向数据流

你可能知道,大多数 web 程序会设计成有两层:视图层和服务层。

Web 环境下视图层主要通过 DOM 结构向用户展示相关程序数据,在 Angular 中这一层是通过组件做的。服务层主要是处理和保存数据,正如上图中展示的,这一层又被切分为状态管理和一些基础部分,如 rest 服务或可重用工具服务(helpers)。

上文中提到的单向数据流指的是视图层,因为它说的是组件,而组件是用来构建视图的:

然而,在随着实现了 redux 架构的 ngrx 引入后,这又让人迷惑了,因为 redux 文档上有 这么一句(译者注:为清晰理解,这句不翻译):

Redux architecture revolves around a strict unidirectional data flow.
This means that all data in an application follows the same lifecycle pattern, making the logic of your app more predictable and easier to understand…

实际上,这个严格单项数据流(strict unidirectional data flow)其实说的是服务层而不是视图层。但我有时会搞混服务层和视图层,会把 redux 的架构模式与 Angular 的结构模式联系起来,当然要避免搞混这两层嗷。Redux 说的单向数据流说的是服务层,而不是视图层嗷。 Redux 主要说的是状态管理模块,会把我们上文说的双向数据流转变为单向数据流。
把双向数据流

转换为单向数据流:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93681.html

相关文章

  • [] 逐渐去掌握 React(作为一名 Angular 开发者)

    摘要:你是一个对感兴趣的开发者吗不用担心,这真的不会让你成为一个背叛者或其他什么,真的。事实上,这个想法并不是或独创的它只是一种很棒的软件开发实践方式。把代码分离到不同的文件里并不会自动导致关注点分离。 原文链接 : Getting to Grips with React (as an Angular developer)原文作者 : DAVE CEDDIA译者 : 李林璞(web前端领域)...

    channg 评论0 收藏0
  • 前端优化感想以及[]redux 教程 第一部分(共四部分

    摘要:自己英语一般,水平有限,献上原文地址,还有我翻译的中文地址,欢迎大家勘误下面是自己的一点感想先说一下,我们知道,前端优化有这么几步,第一步首先呢我们知道,一个应用要依赖好多条文件,而浏览器加载完一条,要执行完这条才加载下一条,所以呢,就很慢 自己英语一般,水平有限,献上原文地址,还有我翻译的中文地址,欢迎大家勘误 下面是自己的一点感想 先说一下webpack,我们知道,前端优化有这么几...

    snowell 评论0 收藏0
  • [] Controller间的数据共享?最佳实践:使用Service

    摘要:共享数据的最佳策略是什么呢用一些变态的控制器继承方案吗当然不是,最简单容易的方式就是使用服务。概括创建一个服务去存放你的数据,并给数据创建和的方法。 原文链接 : Sharing Data Between Controllers? Best Practice: Use a Service原文作者 : DAVE CEDDIA译者 : 李林璞(web前端领域)译者注:翻译如有疏漏,欢迎指出...

    Salamander 评论0 收藏0
  • []Flux入门

    摘要:正式入坑我将按顺序解释概念,并且一个一个地构建它们。视图的和调度员本质上是一个加入了额外规则的事件系统。它来广播事件并注册回调。这描述了我们的,我们称之为。然后,会响应已分派的操作这是处理调度回调的传统方式。 原文地址:https://blog.andrewray.me/flu... ,作者:Andrew Ray TL;DR 当我在努力学习Flux时,我希望有人告诉我:它并不简单,也没...

    Ashin 评论0 收藏0
  • 】每个JavaScript 开发者应该了解的10个面试题

    摘要:避免脆弱的基类问题。红牌警告没有提到上述任何问题。单向数据流意味着模型是单一的事实来源。单向数据流是确定性的,而双向绑定可能导致更难以遵循和理解的副作用。原文地址 1. 你能说出两种对 JavaScript 应用开发者而言的编程范式吗? 希望听到: 2. 什么是函数编程? 希望听到: 3. 类继承和原型继承的不同? 希望听到 4. 函数式编程和面向对象编程的优缺点? ...

    mykurisu 评论0 收藏0

发表评论

0条评论

fox_soyoung

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<