-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathangular笔记.txt
268 lines (238 loc) · 19 KB
/
angular笔记.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
生命周期
constructor OnChanges AfterContentInit AfterViewInit OnDestroy
OnInit AfterContentChecked AfterViewChecked
DoCheck
示例:
<parent [value]="parent">
<child-a [value]="childA"></child-a>
<child-b [value]="childB"></child-b>
</parent>
组件初始化流程
parent constructor
child a constructor
child b constructor
parent ngOnChanges
parent ngOnInit
parent ngDoCheck
child a ngOnChanges
child a ngOnInit
child a ngDoCheck
child b ngOnChanges
child b ngOnInit
child b ngDoCheck
child a ngAfterContentInit
child a ngAfterContentChecked
child b ngAfterContentInit
child b ngAfterContentChecked
parent ngAfterContentInit
parent ngAfterContentChecked
child a ngAfterViewInit
child a ngAfterViewChecked
child b ngAfterViewInit
child b ngAfterViewChecked
parent ngAfterViewInit
parent ngAfterViewChecked
parent ngDoCheck
child a ngDoCheck
child b ngDoCheck
child a ngAfterContentChecked
child b ngAfterContentChecked
parent ngAfterContentChecked
child a ngAfterViewChecked
child b ngAfterViewChecked
parent ngAfterViewChecked
组件更新流程
parent ngOnChanges // 只有当组件的输入属性的值从外部发生变化时触发
parent ngDoCheck // 组件外部的事件也会触发doCheck,因此,doCheck执行的非常频繁,注意优化其中的代码
child a ngOnChanges
child a ngDoCheck
child b ngOnChanges
child b ngDoCheck
child a ngAfterContentChecked // 同doCheck
child b ngAfterContentChecked
parent ngAfterContentChecked
child a ngAfterViewChecked // 同doCheck
child b ngAfterViewChecked
parent ngAfterViewChecked
组件销毁流程
child a ngOnDestroy
child b ngOnDestroy
parent ngOnDestroy
注入器,多个服务、类的容器,实际上就是一个工厂函数,实例化各种服务,每一个组件都有一个自己的注入器
provider,实际上就是服务的一层包装,用来标识服务
装饰器
@NgModule({
declarations: [AppComponent], // 声明本模块拥有的视图类,angular有3种视图类,组件、指令、管道(如:currentHero?.birthdate | date:'longDate')
exports: [AppComponent], // declarations的子集,定义可在其它模块中使用的组件
imports: [BrowserModule], // 本模块组件模板中需要由其它模块导出的类
providers: [Logger], // 在模块providers中定义的服务,会被注册到根注入器,在应用中的任何部分都可被访问到,注意,懒加载模块中通过providers定义的服务,并未注册到根注入器,因此,这些服务只能在子模块中使用,另一种用法:useClass(常用于mock接口、无缝升级等),useExisting(使用另一个已存在的provider,类似定义provider别名,常用于兼容旧组件),useValue,useFactory(常用于需要基于当时的信息动态生成一个依赖值。deps配置项,定义所需依赖)
entryComponents: [AppComponent], // 动态组件,定义在模块声明时需要立刻被编译的组件,angular将会为这些组件创建ComponentFactory,并且将存储这些ComponentFactory到ComponentFactoryResolver中
bootstrap: [AppComponent] // 标识出应用的主视图(被称为根组件),它是所有其它视图的宿主,只有根模块才能设置bootstrap属性
})
@Component({ // 每个组件实例都会自动被添加到其本身对应的注入器的内部容器中,因此可以通过依赖注入查找到知道具体类名的父组件
moduleId: module.id, // 为与模块相关的url(如templateUrl)提供基地址
selector: 'hero-list', // 对于selector名称冲突的组件,可以再封装一层,定义一个新的selector,从而避免重名
templateUrl: 'hero-list.component.html', // 另一种用法template
styleUrls: ['./style.scss'], // 另一种用法style
providers: [HeroService], // 在组件providers中定义的服务,只在该组件及其各级子组件中可用
pipes: [LowercasePipe],
directives: [Tooltip],
animations: [fadeAnimation],
changeDetection: ChangeDetectionStrategy.OnPush
})
变化检测策略,Default、OnPush,开发模式下运行angular,angular会执行checkNoChanges这个方法,去检验之前运行的Change Detection是否导致了别的改动,因此,有些方法会执行两次
OnPush // 在使用OnPush策略后,只有两种情况下(如下),会执行该组件的变化检测,从而更新视图,不执行变化检测,不能更新视图。变化检测用于更新视图,与ngDoCheck等钩子函数无关,ngDoCheck总是触发,父组件的变化检测策略不影响子组件的变化检测策略,父子组件各自独立
1、当组件的input值从外部发生变化时,会触发该组件及其子组件的变化检测
2、该组件内部的事件(如:点击按钮)也会触发该组件及其子组件的变化检测,组件外部的事件(如:setTimeout),不会触发该组件及其子组件的变化检测
Default
1、当组件的input值从外部发生变化时,会触发该组件及其子组件的变化检测
2、该组件内部的事件(如:点击按钮)也会触发该组件及其子组件的变化检测
3、组件外部的事件(如:setTimeout),也会触发该组件及其子组件的变化检测
@Directive({
selector: '[ngModel]:not([formControlName]):not([formControl])',
host: {
'(click)': 'onClick($event.target)',
'role': 'button',
'[class.pressed]': 'isPressed'
},
providers: [formControlBinding],
exportAs: 'ngModel' // 导出指令实例,使得可以在模板中引用指令的实例,如#i="ngModel"
})
@Pipe() // 配置项,pure,默认为true。Pure Pipe:如果传入Pipe的参数没有变,会直接返回之前一次的结果,InPure Pipe:每一次Change Detection都会重新运行Pipe内部的逻辑并返回结果
@Injectable() // 表示一个类可以由注入器进行创建,即该类的构造器参数可以通过注入器注入依赖。@Component、@Directive、@Pipe都是@Injectable的子类,因此组件无需再装饰@Injectable()。providedIn配置项,{ModuleClass} | 'root' | null,设置注入器级别
@Inject(APP_CONFIG) // let APP_CONFIG = new InjectionToken('app.config'),'app.config',token的描述
@RouteConfig() // 定义路由
@Optional() // 依赖注入时,依赖可选非必须
@SkipSelf() // 依赖注入时,跳过依赖自身,防止循环依赖
@Self() // 依赖注入时,依赖自身
@Input()
@Output()
@ViewChild() // read查询
@ViewChildren() // QueryList<T>
@ContentChild()
@ContentChildren()
@HostListener() // 为宿主元素添加事件监听, @HostListener('click', ['$event.target', ...]),@HostListener('document:click', ['$event'])
@HostBinding() // 动态设置宿主元素的属性值,@HostBinding('attr.role') role = 'button',@HostBinding('class.pressed') isPressed: boolean
ng-content // select属性,不能在root component中使用
ng-template // ngTemplateOutlet属性,ngTemplateOutletContext属性($implicit)
ng-container // *ngComponentOutlet指令,用于动态创建组件,*ngComponentOutlet="componentTypeExpression; injector: myInjector; content: myContent; ngModuleFactory: myModule;"
injector // 可选,component需要注入其它服务时使用,this.myInjector = ReflectiveInjector.resolveAndCreate([TestService, ...], this.injector); this.injector为Injector类的实例,通过constructor依赖注入
content // 可选,component模板中有ng-content时使用,this.myContent = [[document.createTextNode('hello')], [document.createTextNode('world')]]; 注意是二维数组,第一维的数组索引,代表ng-content的位置,第二维为该ng-content中要存放的内容
ngModuleFactory // 可选,component来自其它模块时使用,this.myModule = this.compiler.compileModuleSync(OtherModule); this.compiler为Compiler类的实例,通过constructor依赖注入,OtherModule不需要事先导入到AppModule中,this.myModule的类型为NgModuleFactory<any>
forwardRef // 让我们可以在使用构造函数注入时,使用尚未定义的依赖对象类型(定义的class未提升),@Inject(forwardRef(() => Buffer)) private buffer,还可以用来去除循环引用,TypeScript中只能引用已经定义了的对象,因此当class A引用class B,而class B又引用class A时,会出错,forwarRef()函数会返回一个非直接引用
ChangeDetectorRef // 组件的变化检测器的引用,markForCheck()(下一个周期,强制执行变化检测,注意:会对整个应用进行变化检测),detach(),reattach(),detectChanges()(只对该组件及其各个子组件执行变化检测)
ElementRef // 封装不同平台下视图层中的native元素
ComponentRef // 组件的引用
TemplateRef // 模板的引用,TemplateRef实例中引用ElementRef(comment元素的封装),createEmbeddedView(null)返回embeddedViewRef
ViewRef // 视图对象,表示angular视图元素
EmbeddedViewRef // 内嵌视图,表示模板中定义的视图元素,rootNodes属性包含了模板的所有节点,通常与TemplateRef配合使用,继承自ViewRef
ViewContainerRef // 表示一个视图容器(当作兄弟节点,而非父节点),用于创建和管理内嵌视图(TemplateRef)或组件视图(ComponentRef),实例中引用ElementRef,createEmbeddedView(templateRef)简化通过模板创建视图,createComponent(...)
动态组件 // https://zhuanlan.zhihu.com/p/60062423
动态创建并把内容添加到视图的过程大概如下:
1、创建组件类,定义组件需要的属性,并用装饰器装饰类
2、创建模块类,把上面的组件加到模块声明里,用装饰器装饰模块类
3、编译器编译模块和所有组件,获得组件工厂函数
所有的组件工厂函数都是编译器使用@Component装饰器提供的元数据生成的。编译器收集必要的信息并把这些信息封装在组件工厂函数中
通过工厂函数可以很容易的创建一个组件的实例,然后使用ViewContainerRef插入到dom
angular组件必须属于一个模块,组件自己无法独立存在,如果使用另一个模块的组件,在当前模块必须先import另一个模块,如果一个模块想暴露一些组件供其它模块使用,则必须在这个模块里先导出
angular组件只能属于一个唯一的模块,如果在另一个模块里再次声明这个组件,那么就会收到一个编译错误
angular编译程序的时候,会取得所有的组件,包括模块的entryComponents属性里的和组件模板里的,然后为这些组件生成工厂
所有的模块都提供了一个service来给它的组件来访问某组件(该组件需在该模块中声明或者该组件已经被导入)的工厂函数,这个service就是ComponentFactoryResolver
通过Compiler实例,可以手动编译模版字符串,不需要预先定义模块和组件,从而可以更极客、灵活的创建动态组件
如果组件是手动创建的,那么父组件销毁的时候不要忘了在ngOnDestroy中手动销毁所有手动创建的组件
所有动态加载的组件,和静态的组件一样angular会提供变更检测,也就是说ngDoCheck钩子函数会被调用,但是ngOnChanges钩子不会触发,即使动态组件提供了@Input并且父组件的相应属性发生了变化,这是因为编译期间编译器会生成执行输入检查的函数,这个函数是组件工厂的一部分,并且它只能根据模板的信息生成,又因为我们在静态模板中没有使用动态组件,因此这个函数在编译期间没有生成
动态模块加载和编译 // https://zhuanlan.zhihu.com/p/60062423
运行时加载模块有两种方法:
1、用angular的SystemJsNgModuleLoader,如果使用SystemJS做为loader的话,经常被用来加载子路由,使用公开方法load把模块加载到浏览器,编译模块及其所有的组件
该方法有一个问题,SystemJsNgModuleLoader调用的是编译器的compileModuleAsync方法,这个方法只会为在entryComponents里声明的或者组件模板里的组件创建工厂函数
2、为弥补方法一的问题,编译所有组件,可手动调用Compiler实例的compileModuleAndAllComponentsAsync函数,这个函数会为该模块上的组件生成工厂,并将它们们作为ModuleWithComponentFactories,但是这种加载模块的方式,使用的是angular并不推荐的非公开api
运行时编译(JIT),运行前编译(AOT)。其实在angular里只有一种编译器,具体是JIT还是AOT取决于你在什么时候使用它,如果在浏览器中运行代码时使用那就是JIT,如果在运行程序前把所有的组件都编译好,那么就是AOT,但是后者有很多好处比如更快的渲染速度和更小的代码体积
使用AOT意味着代码在运行时不会再有编译器(提前编译好,运行时不要编译器了),如果项目中只使用了ComponentFactoryResolver,那么代码还可以运行,但是动态编译组件(手动调用Compile实例方法)就无法正常运行了,可通过引入JitCompilerFactory解决此问题
form // form标签上,标注属性novalidate,禁用浏览器原生验证
模板驱动式表单 // FormsModule
ngForm // 可通过#f="ngForm",将ngForm实例赋值给变量f
ngModelGroup // 对表单输入内容进行分组,绑定嵌套的字段,可通过#g="ngModelGroup",将ngModelGroup实例赋值给变量g
ngModel // 数据双向绑定,可通过#i="ngModel",将ngModel实例赋值给变量i,<input [(ngModel)]="user.name"> == <input [ngModel]="user.name" (ngModelChange)="user.name = $event">
ngSubmit // 输出属性,表单提交时触发
required
minlength
maxlength
email
pattern
...
响应式表单 // ReactiveFormsModule
FormControl // 用于跟踪组件的值和验证状态
formControl // 在模板中使用,绑定某FormControl实例
formControlName // 在模板中使用,绑定FormGroup对象中对应的FormControl实例,此时input等组件可不指定name属性
FormGroup
formGroup // 在模板中使用,绑定某FormGroup实例
formGroupName // 在模板中使用,绑定FormGroup对象的嵌套FormGroup实例
FormArray // 需在FormGroup内部使用
formArrayName // 在模板中使用,绑定FormGroup对象中对应的FormArray实例
FormBuilder // group(),control(),array()等方法,方便创建FormGroup、FormControl、FormArray实例
ngSubmit
自定义验证器函数 // 可定义一个Directive,implements Validator,实现validate方法,参数AbstractControl,验证通过,返回null,不通过,返回一个只包含一个属性的对象如:{abc: boolean | string | Object ...} ,在模板中通过调用hasError('abc')判断是否通过,可以通过getError('abc', path?: string[])获取错误信息,path为该AbstractControl实例在FromGroup中的层级结构
同步验证器 // provide: NG_VALIDATORS, multi: true
异步验证器 // provide: NG_ASYNC_VALIDATORS, multi: true,监听AbstractControl实例的valueChanges,返回一个Observable对象
动画
对于动态创建的component,调用destroy方法,:leave animation不触发。:leave,只能在host元素上触发
在ngFor循环中使用trackBy优化列表性能,*ngFor="let hero of heroes; trackBy: myTrackByFn;",trackBy方法的第一个参数是当前元素在数组中的index,第二个是当前元素本身(hero)。方法的返回值作为当前元素的唯一标识,用于索引
shadow piercing
:host // 相当于[hostName]
:host(selector) // 相当于selector[hostName]
:host-context(selector) // 相当于selector [hostName] + selector[hostName]
:host ::ng-deep // :host可不加,即全局样式,此时要注意样式的隔离,从angular4.3版本开始,>>>和/deep/被废弃
打包发布命令
ng build --prod --aot --base-href /users --deploy-url /public
创建mono-repo // mono(单),mono-repo,multi-repo
ng new my-workspace --create-application=false --defaults
--create-application // tells the Angular CLI not to generate an initial application
--defaults // tells the Angular CLI not to prompt you about routing and CSS preprocessor
ng generate application my-app1 // will create projects folder
ng generate application my-app2
---
rxjs
Observable, Observer, Subject
Observable
Observable.subscribe(() => {}) // 订阅流,返回Subscription对象,subscription.unsubscribe(),取消订阅
Subject // 继承自Observable
subject.next(value) // 发送数据
subject.error(err) // 触发错误
subject.complete() // 结束subject对象
subject.subscribe(observer) // 添加观察者
subject.unsubscribe() // 取消订阅(设置终止标识符、清空观察者列表)
subject.asObservable() // 返回observable对象
BehaviorSubject(value) // value,初始值,当新增一个观察者的时候,behaviorSubject能够立即发出当前最新的值,而不是没有任何响应
ReplaySubject(n) // 当新增一个观察者的时候,replaySubject能够立即发出当前最新的n个值,而不是没有任何响应,ReplaySubject(1)不等同于BehaviorSubject,因为它不能设置初始值,ReplaySubject只是事件的重放
AsyncSubject() // 在asyncSubject结束后(asyncSubject.complete()),发出最新的值。在asyncSubject结束后,添加新的观察者,asyncSubject能够立即发出当前最新的值,而不是没有任何响应
operator
每个operator都会返回一个新的Observable,不管链式执行多少个operator,最终只有最后一个Observable会被订阅。数据在operator链中,以单值即时传递(即不缓冲)
创建数据流
单值:of, empty, never
多值:from
定时:interval, timer
从事件创建:fromEvent
从Promise创建:fromPromise
自定义创建:create
转换操作
改变数据形态:map, mapTo, pluck
过滤:filter, skip, first, last, take
时间轴上的操作:delay, timeout, throttle, debounce, audit, bufferTime
累加:reduce, scan
异常处理:throw, catch, retry, finally
条件执行:takeUntil, takeWhile, delayWhen, retryWhen, subscribeOn, ObserveOn
转接:switch
组合数据流
concat:保持原来的序列顺序连接两个数据流
merge:合并序列
race:预设条件为其中一个数据流完成
forkJoin:预设条件为所有数据流都完成
zip:取各来源数据流最后一个值合并为对象
combineLatest:取各来源数据流最后一个值合并为数组
从主流转换到从流上
switchMap:主流每发射一次触发了从流的发射,但是在从流发射的过程中,如果主流又一次发射了数据,switchMap会截断上一次的从流,响应本次的主流,常用于连续ajax请求,只响应最新的请求
concatMap:在从流还没有结束的时候,主流还在发射数据,主流会先把发射的数据缓存起来,等从流结束后立即响应主流的数据从而引发新一轮的从流发射,类似消息队列机制
flatMap/mergeMap:即响应主流中发射的每一个数据,它既不会忽略也不会缓存,这就导致主流中数据对应的从流产生了叠加
exhaustMap:在从流还没有结束的时候如果主流仍然有数据在发射,它会忽略此时主流发射的数据
测试
karma + jasmine // angular默认测试框架