我正在重写一个旧指令,我必须使用信号而不是臃肿的RxJS,但我面临着一个我不完全理解的问题.这是指令,它是一个直接监听媒体查询更改的指令:
@Directive({
standalone: true,
})
export class SDKMediaQueriesMixin extends SDKSubscriptionsManager implements OnInit {
private readonly breakpointSignal = signal<Nullable<SDKBreakpoint>>(null);
public readonly breakpoints: LibBreakpointMap<string> = {};
public readonly breakpointNames: LibBreakpointName[] = [];
private matches$: Observable<(MediaQueryListEvent | Partial<MediaQueryListEvent>)[]> = EMPTY;
constructor(@Inject(DOCUMENT) private document: Document, private applicationManager: SDKApplicationManager) {
super();
if (this.applicationManager.isBrowser) {
this.breakpoints.mobile = getComputedStyle(this.document.documentElement).getPropertyValue('--mobile');
this.breakpoints.tablet = getComputedStyle(this.document.documentElement).getPropertyValue('--tablet');
this.breakpoints.desktop = getComputedStyle(this.document.documentElement).getPropertyValue('--desktop');
this.breakpoints.fullhd = getComputedStyle(this.document.documentElement).getPropertyValue('--fullhd');
this.breakpointNames = Object.keys(this.breakpoints) as LibBreakpointName[];
const bpValues = Object.values(this.breakpoints);
const queries = bpValues.map((bp) => `(min-width: ${bp})`);
const matches$ = queries.map((query) => {
const matchMedia = window.matchMedia(query);
return fromEventPattern<Partial<MediaQueryListEvent>>(
matchMedia.addListener.bind(matchMedia),
matchMedia.removeListener.bind(matchMedia)
).pipe(startWith({ matches: matchMedia.matches, media: matchMedia.media }));
});
this.matches$ = combineLatest(matches$);
}
}
ngOnInit() {
if (this.applicationManager.isBrowser && this.matches$) {
this.newSubscription = this.matches$.pipe(
map((events) => events.map((event) => event.matches)),
map((matches) => new SDKBreakpoint(this.breakpointNames[matches.lastIndexOf(true)]))
)
.subscribe((breakpoint) => {
console.log('breakpoint is changing', breakpoint);
this.breakpointSignal.set(breakpoint);
});
}
}
public get activeBreakpoint() {
return this.breakpointSignal();
};
}
在我的AppComponent
中,我想监听活动断点的更改,因此我添加了一个effect
:
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
standalone: true,
imports: [RouterModule, LibAnchorComponent],
hostDirectives: [SDKMediaQueriesMixin]
})
export class AppComponent {
constructor(private mediaQueriesMixin: SDKMediaQueriesMixin) {
effect(() => {
console.log('breakpoint changed', this.mediaQueriesMixin.activeBreakpoint);
});
}
}
该命令在加载时运行两次,以记录以下内容:
app.component.ts:21 breakpoint changed null
media-queries.mixin.ts:67 breakpoint is changing SDKBreakpoint {name: 'fullhd'}
当我更改浏览器窗口的大小时,它会记录以下内容:
media-queries.mixin.ts:67 breakpoint is changing SDKBreakpoint {name: 'desktop'}
media-queries.mixin.ts:67 breakpoint is changing SDKBreakpoint {name: 'tablet'}
media-queries.mixin.ts:67 breakpoint is changing SDKBreakpoint {name: 'fullhd'}
但是,正如您所看到的,我实际上并没有让日志(log)运行.然而,有趣的是,如果我添加一个时间间隔,它只是随机化了一个断点,那么一切都会按预期进行:
interval(1000).pipe(
map(() => new SDKBreakpoint(this.breakpointNames[Math.floor(Math.random() * this.breakpointNames.length)])),
)
.subscribe((breakpoint) => {
console.log('breakpoint is changing', breakpoint);
this.breakpointSignal.set(breakpoint);
});
使用此代码,我将获得以下日志(log):
media-queries.mixin.ts:67 breakpoint is changing SDKBreakpoint {name: 'mobile'}
app.component.ts:21 breakpoint changed SDKBreakpoint {name: 'mobile'}
media-queries.mixin.ts:67 breakpoint is changing SDKBreakpoint {name: 'fullhd'}
app.component.ts:21 breakpoint changed SDKBreakpoint {name: 'fullhd'}
media-queries.mixin.ts:67 breakpoint is changing SDKBreakpoint {name: 'tablet'}
app.component.ts:21
既然this.breakpointSignal.set(breakpoint);
在两种情况下运行相同,我不明白为什么它只在一个间隔内运行……我做错了什么?