zoukankan      html  css  js  c++  java
  • Angular 个人深究(三)【由Input&Output引起的】

    Angular 个人深究(三)【由Input&Output引起的】

    注:最近项目在做别的事情,angular学习停滞了


    1.Angular 中 @Input与@Output的使用

    //test2.component.ts
    import { Component, OnInit,EventEmitter } from '@angular/core';
    import { Input,Output } from '@angular/core';
    @Component({
      selector: 'app-test2',
      templateUrl: './test2.component.html',
      styleUrls: ['./test2.component.css']
    })
    export class Test2Component implements OnInit {
        @Input()
        data_from_parent:string;
        @Output() onTestFunction=new EventEmitter;
        public emitter:any;
      constructor() {
        }
      ngOnInit() {
      }
        onTest(value:string){
            this.onTestFunction.emit(value);
        }
    }
    //test2.component.html
    <div><h1>这是子组件test2</h1></div>
    
    <p style="font-size:12px;">当在父组件的输入框输入数据后,子组件的数据会改变(实现父到子)</p>
    <div>
     来自父组件test1 的数据:{{data_from_parent}}
    </div>
    输入数据传递到父组件:
    <input type="text" (change)="onTest($event.target.value)">
    //test1.component.ts
    import { Component, OnInit  } from '@angular/core';
    @Component({
      selector: 'app-test1',
      templateUrl: './test1.component.html',
      styleUrls: ['./test1.component.css']
    })
    export class Test1Component implements OnInit {
        data_from_child:string;
        ngOnInit() {}
        onChange(value){
            this.data_from_child = value;
        }
    }
    //test1.component.html
    <div>
     <div><h1>这是父组件test1</h1></div>
        <h2>子组件的数据(子到父)</h2>
        <h3>显示子组件的数据:{{data_from_child}}</h3>
    
     <div>
        在input输入数据传递到子组件test2中:
     <input type="text" [(ngModel)]="parent_data" >
        <hr>
     <app-test2 (onTestFunction)="onChange($event)"[data_from_parent]="parent_data" ></app-test2>
     </div>
    </div>

    效果图:

     2.Angular中 @Input 与@Output说明:

    • @Input与@Output 的实现原理,是装饰器,可以参考这个系列的第一篇文章自行探究。
    • @Input() data_from_parent:string; 

        这句话的意义是 告诉Angular 在test2组件中的data_from_parent这个属性是公共的,可以再test1组件中进行绑定,

        在test1.component.html中<app-test2 [data_from_parent]="parent_data"></app-test2>进行了数据绑定,这样test2组件中的data_from_parent就与test1中的parent_data绑定上了。


    3.那么问题来了,再往底层说,是如何实现的数据传递的呢?然后就进行了深入的探索。

      探索方法:到Angular中进行全面的console.log/warn/error 然后分析打印出来的结果

      1)  在test1组件中你输入"1" 之后,打印结果显示,第一个运行到的方法是 zone.js中的 globalZoneAwareCallback 方法。传入的event打印出的结果是:InputEvent请看下图:

      2) 在 globalZoneAwareCallback  函数中调用了 zone.js 中的另一个函数 invokeTask(task, target, event) 三个参数分别是ZoneTask,页面input,InputEvent 看下图:

      3) 在 invokeTask 方法中 直接调用,zonetask类中的 invoke 方法
      4) 定位到zone.js中定义 ZoneTask 的位置,invoke 方法是 return ZoneTask.invokeTask.call(),所以又定位到invokeTask方法

      5) 在 invokeTask 方法中,调用 ZoneTask .zone.runTask (task, target, args)方法 传参分别为,zonetask,页面input,event数组(这里只包含InputEvent)

      6)  然后调用 this._zoneDelegate.invokeTask (此处第一次调用这个方法)
      7)  然后执行 this._invokeTaskZS.onInvokeTask ,定位到 core.js 的 forkInnerZoneWithAngularBehavior 方法,该方法中定义了 onInvokeTask 

      8) 然后调用 ZoneDelegate.invokeTask(此处第二次调用这个方法,但两次执行时的情况不同)

      9) 执行 ZoneTask .callback.apply() 该函数 定位到 platform-browser.js 中的 decoratePreventDefault 方法,传入的参数分别为Event: InputEvent ;eventHandler:core.js中函数renderEventHandlerClosure return的匿名函数。在这个函数中执行 eventHandler(event)

      10) eventHandler 是 core.js的 dispatchEvent 方法,在这个方法中调用 Services.handleEvent ,因为是开发者模式,方法定义调用的 core.js中的 debugHandleEvent

      11) 调用 callWithDebugContext ,将传入的 fn apply,

      12) 退回到 forkInnerZoneWithAngularBehavior 方法,执行 try后的 finally方法 onLeave(),在onleave中执行 checkStable 方法

      13) checkStable(zone) 将zone中的onMicrotaskEmpty.emit。注:onMicrotaskEmpty 的订阅在 core.js中关于 var ApplicationRef 变量的定义的位置。

      14) 执行 订阅时的 this._zone.onMicrotaskEmpty.subscribe中的next函数

      15) next函数中执行NgZone的Run函数,传入的参数是 参数是 this.tick()

      16) 在run 方法中 调用 this._inner.run(),this.inner打印之后 是Zone , 定位到 zone.js的Zone.prototype.run的位置

      17) 在Zone.prototype.run中 调用  this._zoneDelegate.invoke 定位到 zone.js的ZoneDelegate.prototype.invoke 方法
      18) 方法中 判断this._invokeZS是否存在,存在的话,去调用this._invokeDlgt它的ZoneDelegate.prototype.invoke  方法,后者的this._invokeZS 不存在,直接调用callback.apply(),调用callback方法。

      19) callback方法是 刚才 zone.run方法传入的参数,为 this.tick方法,执行它

      20) 首先遍历 this._views 执行 view.detectChange 方法,

      21) 在 detectChange 方法中,最重要的部分是 调用了 Services.checkAndUpdateView ,在这个方法的前后还调用了 this._view.root.rendererFactory.begin与end的方法。但是追踪到方法本身,都是空方法,有待研究。方法位置是platform-browser.js 中的DomRendererFactory2.prototype.begin与DomRendererFactory2.prototype.end

      22) checkAndUpdateView 方法中 有很多方法,我只研究了 印象了页面的方法,通过打断点确认到函数是 execComponentViewsAction 方法

      23) 对于每个view 都会有node结点,在 execComponentViewsAction  方法中将遍历每个结点,检测是否发生页面变化,如果发生就更新,这个方法中调用了callViewAction,通过判断action会有可能递归调用到 checkAndUpdateView  方法,进而更新页面数据。

      24) 如果对页面没有做更新,执行this.tick的方法中,还有一部分代码 会执行 view.checkNoChanges,然后再执行上面的一整套逻辑【有待确认】

      25) 在那之后 还有一段 finally 的代码需要执行,执行函数 wtfLeave(scope)

      26) 在函数 forkInnerZoneWithAngularBehavior的onInvokeTask方法中, 还有一段 finally需要执行,执行函数onLeave(zone)

      27) 执行 checkStable(zone),第二次执行 checkStable,此时 finally中的判断 【if (!zone.hasPendingMicrotasks) {】为 true执行 方法 zone.runOutsideAngular方法,传入参数 【function () { return zone.onStable.emit(null); }】

      28) 调用函数 NgZone._outer.run(fn),_outer 是 zone,调用Zone.prototype.run,定位到zone.js

      29) 调用函数this._zoneDelegate.invoke ,定位到zone.js ,此时要emit的订阅是, _this._ngZone.onStable.subscribe在 core.js中订阅,

      30) 调用函数 scheduleMicroTask,调用 Zone.scheduleTask

      31) 调用函数 this._zoneDelegate.scheduleTask

      32) 在最开始的zoneTask的invokeTask方法中,还有一个finally需要执行,执行函数 drainMicroTaskQueue

      33) 函数中 执行 task.zone.runTask,定位到 zone.js 的 Zone.prototype.runTask

      34) 调用函数 this._zoneDelegate.invokeTask,定位到 ZoneDelegate.prototype.invokeTask

    整个过程大概就是这样,为了重新缕一遍写下,希望对大家也有所帮助。如有错误的部分,希望大神指正,谢谢。

    Stay foolish
  • 相关阅读:
    nginx 主配置文件解析
    redis 主从同步
    redis 持久化 RDB与AOF
    python开发之virtualenv与virtualenvwrapper(linux下安装与配置)
    linux 编译安装python3
    linux基础系统优化及常用命令
    linux基础
    以太坊源码之POA区块生成机制
    以太坊Go、Java、Python、Ruby、JS客户端介绍
    区块链扫盲:区块链技术初探(一)
  • 原文地址:https://www.cnblogs.com/primadonna/p/9267408.html
Copyright © 2011-2022 走看看