在Angular中将每个请求存储到对象中是否属于常规开发实践?
Hey Nick, let's break down your current caching code and talk through its strengths, areas for improvement, and whether it aligns with best practices.
First, the Strengths of Your Approach
Your code has some solid foundations that are in line with good caching practices:
- Shared Subscriptions: Using
publishReplay(1)+refCount()is a smart way to ensure multiple subscribers to the same request only trigger one HTTP call, with subsequent subscribers getting the cached response. This reduces unnecessary network traffic. - Context-Aware Cache Keys: You're combining request params with the default locale to create unique cache keys, which ensures different parameter sets or language contexts don't overwrite each other's cached data—this is a good call.
Areas to Improve (And Why They Matter)
While your core idea is sound, there are a few details that can make the implementation more robust and maintainable:
Incorrect Store Type
You initializedstore = [](an array), but you're using string keys to store Observables. Arrays are designed for numeric indices, so using string keys here is unconventional and can lead to unexpected behavior (like accidental key coercion to numbers). Instead, use an object with explicit typing:private store: { [key: string]: Observable<PostsInt[]> } = {};No Cache Invalidation
Right now, once a request is cached, it stays in memory forever. If the backend data updates, your app will keep serving stale content until the page refreshes. For most applications, adding a cache expiration mechanism is critical. You could modify the store to cache both the Observable and a timestamp, then check if the cache is expired before using it:private store: { [key: string]: { observable: Observable<PostsInt[]>, timestamp: number } } = {}; // When checking the cache: const CACHE_TTL = 300000; // 5 minutes in ms if (this.store[key] && Date.now() - this.store[key].timestamp < CACHE_TTL) { return this.store[key].observable; } else { // Re-fetch and update cache }Unhandled Error Caching
If an HTTP request fails (e.g., network error, server error), the failed Observable will be cached. Every subsequent request for that key will immediately receive the same error, without attempting to re-fetch. Fix this by adding acatchErroroperator to remove the failed entry from the store:posts = posts.pipe( publishReplay(1), refCount(), catchError((error) => { delete this.store[key]; return throwError(() => error); }) );Fragile Cache Key Generation
UsingObject.values(params).join('')can lead to key collisions. For example,{a: '12', b: '3'}and{a: '1', b: '23'}will generate the same key ("123"). Instead, useJSON.stringify(params)to create a unique key that accounts for parameter names and order:const key = JSON.stringify(params) + APP_CONFIG.data.defaultLocale;Potential Memory Bloat
Over time, your store can accumulate a lot of cached Observables, especially if your app has many unique parameter combinations. For long-running SPAs, this can lead to unnecessary memory usage. Consider adding a cleanup mechanism (e.g., periodically removing cached entries that haven't been accessed in a certain time) or using a more memory-efficient structure where possible.
Improved Code Example
Putting all these fixes together, here's a revised version of your function:
import { Observable, throwError } from 'rxjs'; import { publishReplay, refCount, catchError } from 'rxjs/operators'; private store: { [key: string]: { observable: Observable<PostsInt[]>, timestamp: number } } = {}; private readonly CACHE_TTL = 300000; // 5 minutes fetchAll(params: any, cache = true): Observable<PostsInt[]> { if (!cache) { return this.http.get<PostsInt[]>(this.getApiUrl('/posts'), { params }); } const key = JSON.stringify(params) + APP_CONFIG.data.defaultLocale; const now = Date.now(); // Check if valid cache exists if (this.store[key] && now - this.store[key].timestamp < this.CACHE_TTL) { return this.store[key].observable; } // Create and cache the new observable const posts = this.http.get<PostsInt[]>(this.getApiUrl('/posts'), { params }).pipe( publishReplay(1), refCount(), catchError((error) => { delete this.store[key]; // Remove failed cache entry return throwError(() => error); }) ); this.store[key] = { observable: posts, timestamp: now }; return posts; }
Final Verdict
Your core approach to caching HTTP requests is absolutely a good practice—it reduces network calls and improves app performance. With the tweaks above, you'll make the implementation more robust, avoid edge cases, and align it with production-ready best practices.
内容的提问来源于stack exchange,提问作者Nick

