Javascript 将角度组件从多个输入/输出重构到单个配置对象
我的组件通常从具有多个Javascript 将角度组件从多个输入/输出重构到单个配置对象,javascript,angular,angular-components,Javascript,Angular,Angular Components,我的组件通常从具有多个@输入和@输出属性开始。在添加属性时,切换到单个配置对象作为输入似乎更为简洁 例如,这里有一个具有多个输入和输出的组件: export class UsingEventEmitter implements OnInit { @Input() prop1: number; @Output() prop1Change = new EventEmitter<number>(); @Input() prop2: number; @Outp
@输入
和@输出
属性开始。在添加属性时,切换到单个配置对象作为输入似乎更为简洁
例如,这里有一个具有多个输入和输出的组件:
export class UsingEventEmitter implements OnInit {
@Input() prop1: number;
@Output() prop1Change = new EventEmitter<number>();
@Input() prop2: number;
@Output() prop2Change = new EventEmitter<number>();
ngOnInit() {
// Simulate something that changes prop1
setTimeout(() => this.prop1Change.emit(this.prop1 + 1));
}
}
模板:
<using-event-emitter
[(prop1)]='prop1'
(prop1Change)='onProp1Changed()'
[(prop2)]='prop2'
(prop2Change)='onProp2Changed()'>
</using-event-emitter>
<using-config [config]="config" (configChange)="onConfigChange($event)"></using-config>
及其用法:
export class AppComponent {
prop1 = 1;
onProp1Changed = () => {
// prop1 has already been reassigned by using the [(prop1)]='prop1' syntax
}
prop2 = 2;
onProp2Changed = () => {
// prop2 has already been reassigned by using the [(prop2)]='prop2' syntax
}
}
export class AppComponent {
config = {
prop1: 1,
onProp1Changed(val: number) {
this.prop1 = val;
},
prop2: 2,
onProp2Changed(val: number) {
this.prop2 = val;
}
};
}
模板:
<using-config [config]='config'></using-config>
现在,我可以通过多层嵌套组件传递配置对象引用。使用配置的组件将调用回调,如
config.onProp1Changed(…)
,这将导致config对象重新分配新值。所以我们似乎仍然有单向数据流。另外,添加和删除属性不需要在中间层中进行更改
使用单个配置对象作为组件的输入,而不是使用多个输入和输出,有什么缺点吗?避免像这样的
@Output
和EventEmitter
会不会引起我以后可能遇到的任何问题?如果我个人认为我需要4个以上的输入+输出,我会再次检查我创建组件的方法,也许它应该是多个组件,我做错了什么。
无论如何,即使我需要那么多的输入和输出,我也不会在一个配置中实现,原因如下:
1-很难知道输入和输出内部应该是什么,如下所示:
(考虑一个包含html输入元素和标签的组件)
想象一下,如果你只有3个组件,你应该在1到2个月后重新开始这个项目,或者其他人会与你合作或使用你的代码!。
你的代码真的很难理解
2-缺乏绩效。对于angular来说,观察单个变量要比观察数组或对象便宜得多。除此之外,我首先给出了一个例子,为什么您应该强制跟踪标签,这些标签可能永远不会随着总是在变化的值而改变。 3-更难跟踪变量和调试。angular本身带有难以调试的令人困惑的错误,为什么我要让它更难调试呢。对我来说,一个接一个地跟踪和修复任何错误的输入或输出比在一个配置变量中跟踪和修复一堆数据更容易 就我个人而言,我更喜欢将我的组件分解成尽可能小的组件,并对每个组件进行测试。然后用小部件制造大部件,而不是只有一个大部件 更新: 我将此方法用于一次输入且不更改数据(如标签) Html:
使用此方法,angular将不会跟踪组件内部或外部的任何更改。
但使用@input方法,若您的变量在父组件中更改,那个么您也将在组件内部得到更改
使用单个配置对象作为组件的输入,而不是使用多个输入和输出,有什么缺点吗
是的,当您想要切换到时(在较大的项目中通常需要切换到),以缓解由于渲染周期过多而导致的性能问题,angular将检测不到配置对象内部发生的更改
像这样避免@Output和EventEmitter会不会引起我以后可能遇到的任何问题
是的,如果您开始远离@Output
,并在模板中直接对配置对象本身进行操作,则会在视图中产生副作用,这将是将来难以发现的错误的根源。您的视图永远不应该修改它所注入的数据。从这个意义上讲,它应该保持“纯粹”,并且只通过事件(或其他回调)通知控制组件发生了什么
更新:再次查看文章中的示例后,您的意思似乎不是要直接对输入模型进行操作,而是要通过config对象直接传递事件发射器。通过@input
(这是您隐式执行的操作)传递回调也具有以下功能,例如:
- 您的组件变得更难理解和推理(它的输入和输出是什么?)
- 不能再使用了
输入
s使用单个配置对象是可以的,但是您应该始终坚持输出
s<代码>输入定义组件从外部需要什么,其中一些可能是可选的。但是,Output
s完全是组件的业务,应该在中定义。如果依赖用户传递这些函数,则必须检查未定义的
函数,或者直接调用这些函数,就好像它们总是在配置中传递一样,如果有太多的事件需要定义,即使用户不需要它们,使用组件可能会很麻烦。因此,始终在组件中定义输出
s,并发射需要发射的任何东西。如果用户不绑定那些事件的函数,那没关系
另外,我认为为输入设置单个config
不是最佳做法。它隐藏了真正的输入,用户可能需要查看代码或文档的内部,以找出他们应该传递的内容。但是,如果您的输入是单独定义的,那么用户可以通过以下工具获得一些智能感知:
此外,我认为它也可能破坏变化检测策略
让我们看看下面的例子
@组件({
选择器:“我的公司”,
模板:`
{{config.b+config.c}
`
})
导出类MyComponent{
@Input()配置;
}
让我们使用它
@组件({
选择器:“您的公司”,
模板:`
`
})
导出类组件{
配置={
a:1,b:2,c:3
};
}
和用于单独输入
@组件({
选择器:“我的公司”,
@Component({
selector: 'icon-component',
templateUrl: './icon.component.html',
styleUrls: ['./icon.component.scss'],
inputs: ['name', 'color']
});
export class IconComponent implements OnInit {
name: any;
color: any;
ngOnInit() {
}
}
<icon-component name="fa fa-trash " color="white"></icon-component>
export class UsingConfig implements OnInit {
@Input() config: any;
@Output() configChange = new EventEmitter<any>();
ngOnInit() {
// Simulate something that changes prop1
setTimeout(() =>
this.configChange.emit({
...this.config,
prop1: this.config.prop1 + 1
});
);
}
}
<using-config [config]="config" (configChange)="onConfigChange($event)"></using-config>
export class AppComponent {
config = {prop1: 1};
onConfigChange(newConfig: any){
// if for some reason you need to handle specific changes
// you could check for those here, e.g.:
// if (this.config.prop1 !== newConfig.prop1){...
this.config = newConfig;
}
}
export class Base{
@Input() prop1: number;
@Output() prop1Change = new EventEmitter<number>();
@Input() prop2: number;
@Output() prop2Change = new EventEmitter<number>();
}
@Component({})
export class MyComponent extends from Base{
constructor(){super()}
}
export function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
export class MyMixin{
@Input() prop1: number;
@Output() prop1Change = new EventEmitter<number>();
@Input() prop2: number;
@Output() prop2Change = new EventEmitter<number>();
}
applyMixins(MyComponent, [MyMixin]);
export class MyComponent{
@Input() prop1: number = 10; // default
}