使用 Angular 进行客户端缓存
应用程序向用户显示有用信息的时间对用户体验有很大影响。因此,我认为作为软件开发者,我们有责任实现一些机制,尽可能减少加载时间。
在本文中,我将向您展示如何使用 Angular 实现客户端缓存。
读完本文后,您将能够像这样缓存您的 http 请求:
return this._http.get<Product[]>({ url: 'https://example-api/products', cacheMins: 5 })
对于此实现,我们需要:
- 缓存服务:此服务主要有两个用途:
- 将数据保存在本地存储中(有有效期)
- 从本地存储加载数据。
- 自定义http-client 服务:此服务将在底层使用 Angular HttpClient,但也将使用上面提到的缓存服务从本地存储获取和保存数据。
缓存.服务.ts
import { Injectable } from '@angular/core'
@Injectable()
export class CacheService {
constructor() { }
save(options: LocalStorageSaveOptions) {
// Set default values for optionals
options.expirationMins = options.expirationMins || 0
// Set expiration date in miliseconds
const expirationMS = options.expirationMins !== 0 ? options.expirationMins * 60 * 1000 : 0
const record = {
value: typeof options.data === 'string' ? options.data : JSON.stringify(options.data),
expiration: expirationMS !== 0 ? new Date().getTime() + expirationMS : null,
hasExpiration: expirationMS !== 0 ? true : false
}
localStorage.setItem(options.key, JSON.stringify(record))
}
load(key: string) {
// Get cached data from localstorage
const item = localStorage.getItem(key)
if (item !== null) {
const record = JSON.parse(item)
const now = new Date().getTime()
// Expired data will return null
if (!record || (record.hasExpiration && record.expiration <= now)) {
return null
} else {
return JSON.parse(record.value)
}
}
return null
}
remove(key: string) {
localStorage.removeItem(key)
}
cleanLocalStorage() {
localStorage.clear()
}
}
export class LocalStorageSaveOptions {
key: string
data: any
expirationMins?: number
}
http-客户端.服务.ts
import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { CacheService } from './cache.service'
import { Observable, of } from 'rxjs'
import { switchMap } from 'rxjs/operators'
export enum Verbs {
GET = 'GET',
PUT = 'PUT',
POST = 'POST',
DELETE = 'DELETE'
}
@Injectable()
export class HttpClientService {
constructor(
private http: HttpClient,
private _cacheService: CacheService,
) { }
get<T>(options: HttpOptions): Observable<T> {
return this.httpCall(Verbs.GET, options)
}
delete<T>(options: HttpOptions): Observable<T> {
return this.httpCall(Verbs.DELETE, options)
}
post<T>(options: HttpOptions): Observable<T> {
return this.httpCall(Verbs.POST, options)
}
put<T>(options: HttpOptions): Observable<T> {
return this.httpCall(Verbs.PUT, options)
}
private httpCall<T>(verb: Verbs, options: HttpOptions): Observable<T> {
// Setup default values
options.body = options.body || null
options.cacheMins = options.cacheMins || 0
if (options.cacheMins > 0) {
// Get data from cache
const data = this._cacheService.load(options.url)
// Return data from cache
if (data !== null) {
return of<T>(data)
}
}
return this.http.request<T>(verb, options.url, {
body: options.body
})
.pipe(
switchMap(response => {
if (options.cacheMins > 0) {
// Data will be cached
this._cacheService.save({
key: options.url,
data: response,
expirationMins: options.cacheMins
})
}
return of<T>(response)
})
)
}
}
export class HttpOptions {
url: string
body?: any
cacheMins?: number
}
现在,假设我们有一个产品服务,用于从 API 中检索产品列表。在此服务中,我们将使用最近创建的 http-client 服务发出请求,并将数据在 localstorage 中保存 5 分钟:
// product.service.ts
import { Injectable } from '@angular/core'
import { HttpClientService } from './http-client.service'
import { Observable } from 'rxjs'
@Injectable()
export class ProductService {
constructor(
private _http: HttpClientService
) { }
getAll(): Observable<Product[]> {
return this._http
.get<Product[]>({ url: 'https://example-api/products', cacheMins: 5 })
}
}
export class Product {
name: string
description: string
price: number
available: boolean
}