带有 @Component()
装饰器的 TypeScript
类是 Angular
组件组成非常重要的一部分,常用的属性有以下几种
- selector: CSS 选择器,用于定义如何在模板中使用组件
- template或者templateUrl: HTML 模板
- styles或styleUrls: 一组可选的 CSS 样式
还有一些不太常用的属性,我们一起来看看有哪些。
changeDetection
使用方式:
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
changeDetection
用于给当前组件定义变更检测策略,当组件实例化之后,Angular
就会创建一个变更检测器,它负责传播组件各个绑定值的变化。源码中它的值为:changeDetection?: ChangeDetectionStrategy
,我们再来看看源码中ChangeDetectionStrategy是如何定义的
export declare enum ChangeDetectionStrategy {
OnPush = 0,
Default = 1
}
Default
: 变更策略为 CheckAlways,默认值。
这个没什么好说的,始终检测,即使组件内的属性和@Input属性都没有变化,也会不断检测变更OnPush
: 变更策略为 CheckOnce(按需)。
使用 ChangeDetectionStrategy.OnPush
也是优化性能的一种方法,毕竟它是按需检测嘛,不会不停的检测,但是使用它是有严格的条件的,不然一不小心就产生bug了,如果业务满足以下条件,可以放心使用:
- 组件的视图变化仅仅依赖
@Input
属性的变化,如果有依赖组件本身的属性则不可以 - 当
@Input
属性为非对象的时候,或者为对象的时候的每次@Input
属性的改变是改变对象本身的方式(eg: obj = xxx
),而不是改变引用的方式(eg: obj.propertyName = xxx
)。
如果 @Input
属性的改变是改变引用的方式,还是想触发视图检测更改,还可以使用 ChangeDetectorRef
手动触发变更。
注意: Angular
每次做变更检测都是组件树从上至下,如果父组件设置了 OnPush
时,会按照此策略检测父组件以及其子组件,此时子组件即使设置为Default
也是无效的。而且由于父组件为 OnPush
,会导致传输给子组件的属性即使变化了也不会更新到子组件(因为视图没有重新渲染),使用它一定要考虑特别周全
哈哈,感觉越讲越多,changeDetection
可以单独写一篇了,如果大家有需要了解的,我再单独开一篇。好了,the next one~
viewProviders
提到这个就需要了解依赖注入,但是关于DI
需要学习的非常多,值得单独讲解,本章的重点不是DI
,所以不会讲太多。viewProviders
在源码中的值为: viewProviders?: Provider[];
表示定义一组仅在视图可用的Provider对象
注意:前提是该对象没有被注入到根模块,或者组件所在的模块,不然定义 viewProviders
显得毫无意义,如下我们有一个 ServiceDemo
service
@Injectable() //注意这里不能加上{providedIn: 'root'}不然就注入到根模块了,到处都可以使用
export class ServiceDemo {
...
}
我们把 ServiceDemo
加入 ChildComponent
组件的 viewProviders
, ServiceDemo
既没有被注入根模块也没有被注入 ChildComponent
所在的模块
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss'],
viewProviders: [ServiceDemo]
})
export class ChildComponent implements OnInit {
...
}
我们只能在 ChildComponent
的视图使用 ServiceDemo
的属性或者方法,如果在组件内使用就会报错。
providers
接以上,如果我们想在 ChildComponent
的视图和组件内使用 ServiceDemo
的属性或者方法,或者仅在组件内使用,则应该把它加入 providers
,如下:
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss'],
providers: [ServiceDemo]
})
export class ChildComponent implements OnInit {
...
}
使用 viewProviders
或者 providers
,目的是只把 provider
对象注入到某个组件或者组件视图,这样的 provider
对象一定是小范围内使用的封装对象,避免注入到根模块影响首屏加载。
encapsulation
用于定义 Component
的模板和样式封装选项。encapsulation
在源码中的值为 encapsulation?: ViewEncapsulation
,来看看 ViewEncapsulation
如何定义的
export declare enum ViewEncapsulation {
Emulated = 0, // 默认
Native = 1, // 已废弃
None = 2,
ShadowDom = 3
}
Emulated:这是默认选项,会向
Host
元素添加_nghost-xxx-xxx
,非Host
元素添加_ngcontent-xxx-xxx
属性模拟原生选择器,用来替代class
和id
,以隔离组件的样式不影响外部,组件内定义的样式作用域仅在该组件。<app-parent _nghost-uvk-c19> <h1 _ngcontent-uvk-c19>parent component</h1> <p _ngcontent-uvk-c19>parent works!</p> <app-child _ngcontent-uvk-c19 _nghost-uvk-c20> <p _ngcontent-uvk-c20>child works</p> </app-child> </app-parent>
- None :不提供任何模板或样式封装,意味着该组件内的样式会被添加到全局,应用到整个
document
。 - ShadowDom:使用原生的
Shadow DOM
特性封装,并为组件的Host
元素创建 ShadowRoot,组件样式将不受全局样式影响,组件内定义的样式作用域限定为该Shadow DOM
。使用了ShadowDom
组件的子组件也会应用到该组件样式,因为子组件也在ShadowRoot
中,ShadowDom
可应用于微前端样式隔离。
interpolation
改写默认的插值表达式起止分界符({{ 和 }})。它的值定义为 interpolation?: [string, string]
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
interpolation: ['(', ')']
})
如果你不喜欢使用{{ value }}
这样的插值方式,可以改成你喜欢的方式eg: (value)
preserveWhitespaces
它的值为 Boolean
,默认为 false
,编译后的模板中移除可能多余的空白字符。 空白字符就是指那些能在 JavaScript 正则表达式中匹配 \s 的字符。为 true 则保留。
entryComponents
一个组件的集合,它应该和当前组件一起编译。对于这里列出的每个组件,Angular 都会创建一个 ComponentFactory
并保存进 ComponentFactoryResolver
中, 官方的解释,是不是看完还是不知道是干嘛的?跟我一起了解一下
假设你在项目定义了一些组件,这些组件编译工具没有识别出来它在项目中有使用,Tree Shaking 工具就会把这些组件从最终的代码包中摘出去。但是其实这些组件可能是路由组件或者是动态加载的,必须把它们显式添加到 entryComponents
中, 路由组件需要添加到 @NgModule
的 entryComponents
中,动态加载的组件看它的使用范围,如果仅仅在一两个组件内使用了,可以把它们加入组件的 entryComponents
中
animations
用来引入定义动画的触发器,需要结合 @angular/animations
使用,本章就不重点讲解
moduleId
包含该组件的那个模块的ID。在使用CommonJS 作为模块化工具的时候用得到,angular
使用的是es6
模块化语法,日常开发估计用不到这个属性。
以下是继承自 @Directive
的属性
inputs
列举一组可供数据绑定的输入属性,作用类似 @Input
,Angular 会在变更检测期间自动更新输入属性,使用方法如下:
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss'],
inputs: ['name', 'id: child-id']
})
export class ChildComponent implements OnInit {
name: string;
id: number; // 指代child-id的值
}
<app-child [name]="'child'" [child-id]="1"></app-child>
使用inputs的时候编辑器还会提示 Use @Input rather than the inputs metadata property
,看来官方也不推荐 inputs
,使用 @Input
可以定义私有或共有,还可以定义数据类型,实在找不到inputs存在的意义哈哈~~
或许不用引入 @Input
,仅仅查看 @Component
装饰器的配置就能知道输入属性是它的优点,也或许它又其他的优势,只是我技术有限还还没发现哈哈~~~
outputs
同inputs类似,功能同@Output一样,用法如下:
@Component({
selector: 'app-child',
outputs: [ 'bankNameChange' ]
template: `<input (input)="bankNameChange.emit($event.target.value)" />`
})
class ChildDir {
bankNameChange: EventEmitter<string> = new EventEmitter<string>();
}
<app-child (bankNameChange)="onBankNameChange($event)"></app-child>
exportAs
定义一个名字,用于在模板中把该指令赋值给一个变量。Component
虽然继承了 Directive
这个属性,但我觉得这个属性在 Component
发挥不了什么特别的作用,在 Directive
还是有点用处的。
假设我们定义了一个 child-dir
指令
@Directive({
selector: 'child-dir',
exportAs: 'child'
})
class ChildDir {
}
现在我们在模板中使用它
<div child-dir #c="child" #c1></div>
在组件中获取它们
@ViewChild('c') child;
@ViewChild('c1') child1;
打印它们可以发现c为 child-dir
指令的实例, c1为 ElementRef
实例。这时好像还是不明白它的具体用处。ElementRef
实例可以用来操作DOM, 获取child-dir
实例就可以访问它的属性和方法了,下面就 Angular
的内置指令 NgModel
举例说明。NgModel
指令也用到了 exportAs
值为 ngModel
<input type="text" [(ngModel)]="text" #textModel="ngModel" [required]="true">
// 模板中
<p *ngIf="textModel.hasError('required')" class="error">This field is required</p>
// 组件中
@ViewChild('textModel') textModel: NgModel;
/** 现在可以访问
this.textModel.hasError('required')
this.textModel.valid
this.textModel.disabled
**/
是不是很方便,不用 new FormGroup
或者 FormControl
也能访问我们想要的属性和方法,以上仅适用于简单的表单验证,建议还是使用 new FormGroup
创建一个 Form
对象对表单进行处理,让代码更具有可读性。
queries
配置一些查询,它们将被注入到该指令中。
作用同 @ViewChildren
,@ContentChildren
,@ViewChild
,@ContentChild
类似
host
使用一组键-值对,把类的属性映射到宿主元素的绑定(Property、Attribute 和事件)。
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
host: {
'name': 'app-child',
'onclick': 'return false' // 表示调用DOM 事件的 preventDefault 函数,这个语句中可以引用局部变量 event 来获取事件数据
}
})
// 渲染结果
<app-child _ngcontent-kge-c48 name="app-child" onclick="return false" _nghost-kge-c49></app-child>
jit
如果存在,则该指令/组件将被 AOT 编译器忽略。它会保留在发布代码中,并且 JIT 编译器会尝试在运行时在浏览器中对其进行编译。为了确保其行为正确,该应用程序必须导入 @angular/compiler 。
喜欢就点个赞吧~~~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。