Node + Angular Universal SSR:渲染页面时如何设置device-width
Hey there! Let's figure out how to set the device-width for your Angular Universal app so the pre-rendered pages use the correct mobile or desktop layout. Since you're using ngExpressEngine (just like the Universal Starter), here's a step-by-step solution tailored to your setup:
Step 1: Detect Device Type (Optional but Recommended)
First, you'll want to identify if the incoming request is from a mobile or desktop device. You can use the useragent library for accurate detection, or a simple regex check if you prefer.
First, install the library (if you go that route):
npm install useragent --save
Then, in your server.ts, parse the request's User-Agent header:
const useragent = require('useragent'); // Inside your catch-all route handler app.get('*', (req, res) => { // Parse the user agent const agent = useragent.parse(req.headers['user-agent']); // Flag mobile/tablet devices const isMobile = agent.device.type === 'mobile' || agent.device.type === 'tablet'; // Or use a simpler regex if you don't want the library: // const isMobile = /mobile|android|iphone|ipad/i.test(req.headers['user-agent'] || ''); // We'll pass this flag to Angular next });
Step 2: Inject Device Flag into Angular's SSR Context
Modify your ngExpressEngine configuration to inject the isMobile flag as a provider. This lets your Angular app access the device type during server-side rendering:
app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [ provideModuleMap(LAZY_MODULE_MAP), // Inject our device flag into the Angular app { provide: 'IS_MOBILE', useValue: isMobile } ] }));
Step 3: Dynamically Set Viewport Device-Width in Angular
Now, update your root component (like AppComponent) to adjust the viewport meta tag based on the injected flag. This ensures the server renders the page with the correct layout:
import { Component, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformServer } from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent { constructor( @Inject('IS_MOBILE') private isMobile: boolean, @Inject(PLATFORM_ID) private platformId: object ) { // Only run this on the server if (isPlatformServer(this.platformId)) { const viewportMeta = document.querySelector('meta[name="viewport"]'); if (viewportMeta) { // Set device-width based on device type viewportMeta.setAttribute( 'content', this.isMobile ? 'width=device-width, initial-scale=1.0' : 'width=1200, initial-scale=1.0' // Use your desktop layout width here ); } } } }
Alternative: Simulate Breakpoint Observer for Layouts
If your app uses Angular's BreakpointObserver to handle responsive layouts, you can mock its behavior on the server to match the device type:
Add this provider to your ngExpressEngine config:
import { BreakpointObserver } from '@angular/cdk/layout'; import { of } from 'rxjs'; // Inside the providers array of ngExpressEngine { provide: BreakpointObserver, useFactory: (isMobile: boolean) => { return { observe: () => of({ matches: isMobile, breakpoints: { '(max-width: 767px)': isMobile, // Match your mobile breakpoint '(min-width: 768px)': !isMobile // Match your desktop breakpoint } }) } as BreakpointObserver; }, deps: ['IS_MOBILE'] }
Step 4: Force Device Type for Static Prerendering
If you're using static prerendering (ng run your-project:prerender), you can hardcode the device type or pass it via environment variables:
- Set an environment variable when running prerender:
IS_MOBILE=true ng run your-project:prerender
- Read the variable in
server.ts:
const isMobile = process.env.IS_MOBILE === 'true';
- If you don't need dynamic detection (just want to force mobile/desktop), skip Step 1 and set
isMobiledirectly totrueorfalse. - Make sure your CSS media queries align with the viewport widths you set (e.g.,
@media (max-width: 767px)for mobile). - Test with curl to verify the rendered viewport tag:
# Simulate a mobile request curl -A "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15" http://localhost:4000
内容的提问来源于stack exchange,提问作者JayChase

