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

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

Angular 个人深究(三)【由Input&Output引起的】的更多相关文章

  1. Angular 个人深究(四)【生命周期钩子】

    Angular 个人深究(四)[生命周期钩子] 定义: 每个组件都有一个被 Angular 管理的生命周期. Angular 创建它,渲染它,创建并渲染它的子组件,在它被绑定的属性发生变化时检查它,并 ...

  2. BIOS(Basic Input/Output System)是基本输入输出系统的简称

    BIOS(Basic Input/Output System)是基本输入输出系统的简称 介绍 操作系统老师说,平时面试学生或者毕业答辩的时候他都会问这个问题,可见这个问题对于计算机专业的学生来说是如此 ...

  3. Angular 个人深究(二)【发布与订阅】

    Angular 个人深究(二)[发布与订阅] 1. 再入正题之前,首先说明下[ 发布与订阅模式](也叫观察者模式) 1) 定义:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个 ...

  4. angular学习笔记(三十一)-$location(2)

    之前已经介绍了$location服务的基本用法:angular学习笔记(三十一)-$location(1). 这篇是上一篇的进阶,介绍$location的配置,兼容各版本浏览器,等. *注意,这里介绍 ...

  5. angular学习笔记(三十)-指令(5)-link

    这篇主要介绍angular指令中的link属性: link:function(scope,iEle,iAttrs,ctrl,linker){ .... } link属性值为一个函数,这个函数有五个参数 ...

  6. PHP-FPM-failed to ptrace(PEEKDATA) pid 123: Input/output error

    If you're running PHP-FPM you can see these kind of errors in your PHP-FPM logs. $ tail -f php-fpm.l ...

  7. NFS挂载异常 mount.nfs: Input/output error

    [root@localhost ~]# vi /etc/exports #增加/nfs 192.168.10.132(rw,no_root_squash,no_all_squash,async) [r ...

  8. read()、write()返回 Input/output error, Device or resource busy解决

    遇到的问题,通过I2C总线读.写(read.write)fs8816加密芯片,报错如下: read str failed,error= Input/output error! write str fa ...

  9. Docker 在转发端口时的这个错误Error starting userland proxy: mkdir /port/tcp:0.0.0.0:3306:tcp:172.17.0.2:3306: input/output error.

    from:https://www.v2ex.com/amp/t/463719 系统环境是 Windows 10 Pro,Docker 版本 18.03.1-ce,电脑开机之后第一次运行 docker ...

随机推荐

  1. 记录一个PHP安装redis扩展时的问题

    安装过程:https://www.cnblogs.com/pengyunjing/p/8688320.html 由于我之前安装过该扩展,重新安装时没有执行make clean命令,所以安装好出现了下面 ...

  2. 初识java和C的不同

     学习java语言,发现C语言的语法尽管很类似,但是java的代码编写与C语言却大不相同. 一,java的类,首先接触到的是类这个思想,类中可以定义属性,可以用方法来对属性进行相应的操作: 二,jav ...

  3. 【docx4j】docx4j操作docx,实现替换内容、转换pdf、html等操作

    主要是想要用此功插件操作docx,主要的操作就是操作段落等信息,另外,也想实现替换docx的内容,实现根据模板动态生成内容的效果,也想用此插件实现docx转换pdf. word的格式其实可以用xml来 ...

  4. Jquery中AJAX参数详细介绍

    Jquery中AJAX参数详细列表: 参数名 类型 描述 url String (默认: 当前页地址) 发送请求的地址. type String (默认: "GET") 请求方式 ...

  5. win10安装virtualBox创建CentOS6.5虚拟机

    1.安装virtualBox 1.1.下载安装包,安装 搜索一下,或者去 VirtualBox官网下载一个. 下载第一个,兼容64,32位. 2.创建64位虚拟机 2.1.解决无法创建64位的问题 2 ...

  6. 解决Android SDK下载和更新失败问题

    今天更新sdk报错如下: Failed to fetch URL http://dl-ssl.google.com/android/repository/addons_list-1.xml. 说dl- ...

  7. js的闭包的一个示例说明

    js中 某个函数的内部函数在该函数执行结束后仍然可以访问这个函数中定义的变量,这称为闭包(Closure) 复制代码 代码如下: function outside() { var myVar = 1; ...

  8. IE6下select被这罩住

    在我们做弹出遮罩层时经常遇到这种问题,就是select被这罩住不兼容IE6,其实解决这种问题并不难,只要掌握住原理就挺简单的. 首先就是当遮罩层出现时select要暂时隐藏,但是不能用display: ...

  9. cf276E 两棵线段树分别维护dfs序和bfs序,好题回头再做

    搞了一晚上,错了,以后回头再来看 /* 对于每次更新,先处理其儿子方向,再处理其父亲方向 处理父亲方向时无法达到根,那么直接更新 如果能达到根,那么到兄弟链中去更新,使用bfs序 最后,查询结点v的结 ...

  10. 性能测试九:jmeter进阶之beanshell的使用+断言

    一.使用 BeanShell使用方式一 BeanShell面板上写脚本 // 从vars中获取用户定义的参数,并转换为int类型 int p_skuId = Integer.parseInt(vars ...