面试题答案
一键面试设计跨组件交互自定义指令的架构思路
- 指令定义与分类:
- 父指令:定义在父组件模板上,负责管理整体交互逻辑,维护复杂数据结构。例如,创建一个
ParentInteractionDirective
,在其@Directive
装饰器中指定选择器,如[parentInteraction]
。 - 子指令:定义在子组件模板上,负责接收父指令传递的数据并执行特定行为。比如,为每个需要交互的子组件创建
ChildInteractionDirective
,选择器类似[childInteraction]
。
- 父指令:定义在父组件模板上,负责管理整体交互逻辑,维护复杂数据结构。例如,创建一个
- 数据传递机制:
- 输入属性(@Input()):在子指令中通过
@Input()
装饰器定义输入属性来接收父指令传递的数据。父指令可以将复杂数据结构,如对象或数组,通过这个输入属性传递给子指令。例如,子指令中定义@Input() parentData: any;
,父指令在模板中<child - component [childInteraction][parentData]="complexData"></child - component>
。 - 共享服务(Shared Service):创建一个共享服务来存储和管理需要在多个指令间共享的数据。父指令和子指令都可以注入这个服务,父指令更新数据后,子指令通过订阅服务中的数据变化来获取最新数据。例如,
InteractionService
中定义一个BehaviorSubject
来存储共享数据,父指令注入服务并调用interactionService.sharedData$.next(complexData)
,子指令注入服务并订阅interactionService.sharedData$.subscribe(data => this.localData = data)
。
- 输入属性(@Input()):在子指令中通过
- 行为触发机制:
- 输出属性(@Output()):子指令通过
@Output()
装饰器定义输出属性,当特定行为发生时,通过EventEmitter
发射事件通知父指令。父指令在模板中监听这个事件并执行相应逻辑。比如,子指令中@Output() actionTriggered = new EventEmitter();
,在某个行为触发时this.actionTriggered.emit();
,父指令在模板中<child - component [childInteraction](actionTriggered)="handleChildAction()"></child - component>
。 - 指令间通信:父指令可以通过
ViewChildren
或ContentChildren
查询到子指令实例,直接调用子指令的方法来触发特定行为。在父指令中,@ViewChildren(ChildInteractionDirective) childDirectives: QueryList<ChildInteractionDirective>;
,然后通过this.childDirectives.forEach(directive => directive.doSomeAction())
触发子指令行为。
- 输出属性(@Output()):子指令通过
性能优化
- 内存管理:
- 避免内存泄漏:在指令销毁时,取消所有订阅。例如,在指令的
ngOnDestroy
生命周期钩子中,对于订阅了共享服务数据的情况,调用subscription.unsubscribe();
。 - 对象复用:尽量复用已有的对象实例,而不是每次都创建新的复杂数据结构。比如,在父指令中维护一个可复用的对象池,根据需要从池中获取对象传递给子指令,使用完毕后再放回池中。
- 避免内存泄漏:在指令销毁时,取消所有订阅。例如,在指令的
- 事件监听优化:
- 防抖(Debounce)与节流(Throttle):对于频繁触发的事件,如用户输入或滚动事件,使用防抖或节流技术。可以通过 RxJS 的
debounceTime
或throttleTime
操作符来实现。例如,在子指令中,当监听用户输入事件时,fromEvent(this.elementRef.nativeElement, 'input').pipe(debounceTime(300)).subscribe(() => this.handleInput());
,防止短时间内多次触发不必要的操作。 - 按需添加和移除监听器:只在需要的时候添加事件监听器,在不需要时及时移除。例如,在父指令的
ngOnInit
中添加事件监听器,在ngOnDestroy
中移除监听器,this.renderer.listen(this.elementRef.nativeElement, 'click', this.handleClick.bind(this));
在ngOnDestroy
中this.renderer.removeEventListener(this.elementRef.nativeElement, 'click', this.handleClick.bind(this));
。
- 防抖(Debounce)与节流(Throttle):对于频繁触发的事件,如用户输入或滚动事件,使用防抖或节流技术。可以通过 RxJS 的
- 变化检测优化:
- OnPush策略:对于子组件或指令,将其
ChangeDetectionStrategy
设置为OnPush
。只有当输入属性变化、接收到事件或手动触发markForCheck
时才进行变化检测。在子指令的@Directive
装饰器中添加changeDetection: ChangeDetectionStrategy.OnPush
。这样可以减少不必要的变化检测循环,提高性能。
- OnPush策略:对于子组件或指令,将其