Кэширование ответов в Stimulus используется для сокращения количества сетевых запросов, ускорения отклика интерфейса и стабилизации поведения контроллеров при повторных взаимодействиях. В отличие от полноценных state-менеджеров, Stimulus не навязывает архитектуру хранения данных, поэтому кэширование реализуется на уровне контроллера, модуля или браузерного хранилища.
Кэш может применяться к:
fetch, axios)Самый простой и часто достаточный вариант — хранение данных в свойствах экземпляра контроллера.
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
cache = null
async loadData() {
if (this.cache) {
return this.cache
}
const response = await fetch("/api/items")
const data = await response.json()
this.cache = data
return data
}
}
Особенности:
Такой подход особенно удобен для компонентов, которые многократно запрашивают одни и те же данные в рамках одного экрана.
Если результат зависит от аргументов, используется объект-хранилище
или Map.
cache = new Map()
async loadUser(id) {
if (this.cache.has(id)) {
return this.cache.get(id)
}
const response = await fetch(`/api/users/${id}`)
const user = await response.json()
this.cache.set(id, user)
return user
}
Преимущества Map:
Для разделяемого кэша между несколькими контроллерами используется модульная область видимости.
const responseCache = new Map()
export async function cachedFetch(url) {
if (responseCache.has(url)) {
return responseCache.get(url)
}
const response = await fetch(url)
const data = await response.json()
responseCache.set(url, data)
return data
}
import { Controller } from "@hotwired/stimulus"
import { cachedFetch } from "../lib/cached_fetch"
export default class extends Controller {
async connect() {
this.data = await cachedFetch("/api/settings")
}
}
Применение:
sessionStorage и localStorageДля кэша, сохраняемого между перезагрузками страницы, применяются Web Storage API.
async loadConfig() {
const cached = sessionStorage.getItem("config")
if (cached) {
return JSON.parse(cached)
}
const response = await fetch("/api/config")
const config = await response.json()
sessionStorage.setItem("config", JSON.stringify(config))
return config
}
Отличия хранилищ:
sessionStorage — очищается при закрытии вкладкиlocalStorage — сохраняется бессрочноТакой кэш подходит для:
Кэш без стратегии обновления быстро теряет ценность. Распространённые подходы:
const CACHE_TTL = 60_000 // 1 минута
cache = {
data: null,
timestamp: 0
}
async load() {
const now = Date.now()
if (this.cache.data && now - this.cache.timestamp < CACHE_TTL) {
return this.cache.data
}
const response = await fetch("/api/data")
const data = await response.json()
this.cache = { data, timestamp: now }
return data
}
async refresh() {
this.cache = null
return this.load()
}
Применяется при:
Stimulus-контроллеры часто выполняют вычисления на основе DOM или входных данных. Результаты можно мемоизировать.
get total() {
if (this._total !== undefined) {
return this._total
}
this._total = this.items.reduce((sum, i) => sum + i.price, 0)
return this._total
}
При изменении исходных данных кэш сбрасывается вручную.
При использовании Turbo Frames и Turbo Drive важно учитывать
повторное подключение контроллеров. Локальный кэш контроллера не
переживает навигацию, тогда как модульный или
sessionStorage — переживает.
Типовая схема:
sessionStorageПри агрессивном кэшировании необходимо явно освобождать ресурсы:
disconnect() {
this.cache?.clear?.()
this.cache = null
}
Для Map и больших структур это предотвращает накопление
данных при динамическом DOM.
localStorage для чувствительных
данныхМинимальный кэш внутри контроллера — для UI-оптимизации Модульный кэш — для общих данных Web Storage — для сохранения состояния TTL + ручная очистка — для баланса актуальности и производительности
Кэширование в Stimulus остаётся простым и прозрачным, если данные хранятся рядом с логикой использования и не маскируют реальные источники состояния.