You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Angular Universal服务端渲染时matchMedia无法正常工作

Fixing matchMedia Not Working in Angular Universal

Hey there, let's tackle this matchMedia issue in Angular Universal! The root problem is straightforward: server-side rendering runs in a Node.js environment, which doesn't have access to browser-specific APIs like matchMedia. Here are two solid solutions to get things working again:

Angular's Component Dev Kit (CDK) includes BreakpointObserver—a purpose-built, SSR-friendly tool for detecting media query changes. It’s the cleanest replacement for direct matchMedia calls in Angular apps.

Step 1: Install Angular CDK (if you haven’t already)

npm install @angular/cdk

Step 2: Refactor Your Component Code

Replace your existing matchMedia logic with BreakpointObserver like this:

import { Component, OnInit, ViewChild, ViewChildren, QueryList } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
import { ActivatedRoute, Router } from '@angular/router';
import { BreakpointObserver } from '@angular/cdk/layout';

export class BasicLayoutComponent implements OnInit {
  @ViewChild(MatSidenav) sidenav: MatSidenav;
  @ViewChildren('sidenav') sidenavs: QueryList<MatSidenav>;

  constructor(
    private _route: ActivatedRoute,
    private _router: Router,
    private breakpointObserver: BreakpointObserver
  ) {}

  ngOnInit() {
    // Watch for your small screen breakpoint
    this.breakpointObserver.observe(`(max-width: ${SMALL_WIDTH_BREAKPOINT}px)`).subscribe(result => {
      if (result.matches) {
        // Logic for small screens (e.g., close sidenav)
        this.sidenav.close();
      } else {
        // Logic for large screens (e.g., open sidenav)
        this.sidenav.open();
      }
    });

    // Rest of your ngOnInit logic...
    this._router.events.subscribe(/* ... */);
  }
}

This works flawlessly on both server and client because the CDK handles SSR environment differences internally.

2. Polyfill matchMedia for the Server (Alternative)

If you’d rather stick with matchMedia, you can add a polyfill to simulate browser behavior on the server, plus guard client-only code.

Step 1: Add the Polyfill to server.ts

At the top of your project’s server.ts file, add this to mock matchMedia:

// Polyfill matchMedia for SSR
Object.defineProperty(global, 'matchMedia', {
  writable: true,
  value: (query: string) => ({
    matches: false, // Default to non-mobile on server (adjust as needed)
    media: query,
    onchange: null,
    addListener: () => {},
    removeListener: () => {},
    addEventListener: () => {},
    removeEventListener: () => {},
    dispatchEvent: () => false,
  }),
});

// Rest of your server.ts code...

Step 2: Guard Client-Side Code with isPlatformBrowser

Ensure matchMedia only runs in the browser using Angular’s platform utilities:

import { Component, OnInit, ViewChild, ViewChildren, QueryList, Inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { MatSidenav } from '@angular/material/sidenav';
import { ActivatedRoute, Router } from '@angular/router';

export class BasicLayoutComponent implements OnInit {
  private mediaMatcher: MediaQueryList | undefined;
  @ViewChild(MatSidenav) sidenav: MatSidenav;
  @ViewChildren('sidenav') sidenavs: QueryList<MatSidenav>;

  constructor(
    private _route: ActivatedRoute,
    private _router: Router,
    @Inject(PLATFORM_ID) private platformId: Object
  ) {}

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      this.mediaMatcher = matchMedia(`(max-width: ${SMALL_WIDTH_BREAKPOINT}px)`);
      this.mediaMatcher.addEventListener('change', (mql) => {
        mql.matches ? this.sidenav.close() : this.sidenav.open();
      });
    }

    // Rest of your ngOnInit logic...
    this._router.events.subscribe(/* ... */);
  }
}

Quick Tips

  • Stick with BreakpointObserver if possible—it’s designed for Angular, plays nice with change detection, and avoids manual environment checks.
  • Never call browser APIs directly in SSR without guarding them first—use Angular’s provided utilities to handle platform differences.

内容的提问来源于stack exchange,提问作者Ole Bille

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 10:09:57