Home

angular 单向数据流

stackoverflow (opens new window)中看到个提问

Angular's unidirectional data flow rule forbids updates to the view after it has been composed.
Angular 的单向数据流规则禁止在视图组合后对其进行更新。

网友 :I had to read this sentence about 8 times very slowly and I still don't get it

首先需要重申一下Angular中视图的概念,视图(View)是构建 UI 的最小单元,视图(View)分为 宿主视图(Component)、模板视图(Template)

alt

单向数据流是指 视图树从上往下将数据转换为视图的过程中,不会进一步修改数据。

比如从上到下的变化检测流中,一旦变化检测已经完成了,任何更低层级的 component 都不允许去改变父级的属性。

在实际开发中,有些钩子函数通过@Output去改变 父级 component 的数据值,是允许的,而有些又不允许。

Angular 组件树渲染过程与生命周期的关系:

alt

更新 input bindings,然后会触发 child component 中 OnInit、DoCheck、OnChanges 函数,如果页面有 ng-content,相应也会触发 ngAfterContentInit 和 ngAfterContentChecked。

AfterViewCheckedAfterViewInit是在变更检测(change detetion)之后执行的,这时候数据正在渲染为视图,你又去改变数据,被认为是违反 Angular 的单向数据流。

OnInit、DoCheck、OnChanges 函数是在变更检测之前执行的,这个时候还没有通知组件树去渲染DOM,所以可以对数据进行更改的。

示例: 定义一个父级组件 ComponentA,在这里会显示从 ChildComponent 发过来的 message,代码如下:

@Component({
    template:`<h1>this is child A page</h1>
            <h4>{ { msgFromChild } } </h4>
            <app-child (sendMsgToParent)="getMsgFromChild($event)">
              this is child
            </app-child>`
})

export class ComponentA {
    msgFromChild: any = '';
    getMsgFromChild(value: any) {
        this.msgFromChild = value;
    }
}

定义一个 ChildComponent,通过 @Output 向 ComponentA 发送 message,代码如下:

@Component({
    selector: "app-child",
    template: `<h5 style="color: crimson">this is grand child</h5>
                <ng-content></ng-content>`
})

export class ChildComponent implements  OnInit {
    @Output() sendMsgToParent: EventEmitter<any> = new EventEmitter<any>();
    msg = "hello, change this to parent component"
    ngOnInit() {
        this.sendMsgToParent.emit(this.msg);
    }
}

把 ngOnInit 换成 ngDoCheck、ngAfterContentInit、ngAfterContentChecked、ngOnChanges,效果是一样的,在 ComponentA 中 message 都能正常显示也不会报错。
把 ngOnInit 换成 ngAfterViewInit 或 ngAfterViewChecked 就会报错。