amélioration fluidité + ajout en cache des différentes pages pour chargement plus rapide

This commit is contained in:
innotex
2026-01-16 19:45:07 +01:00
parent c51592c7ea
commit 520166a1e9
7 changed files with 430 additions and 24 deletions

View File

@@ -89,10 +89,20 @@
<!-- Main Content -->
<main class="flex-1 overflow-auto">
<router-view />
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in">
<keep-alive :include="['DashboardView', 'ContainersView', 'DisksView', 'PackagesView', 'ShortcutsView']">
<component :is="Component" :key="route.path" />
</keep-alive>
</transition>
</router-view>
</main>
</div>
<router-view v-else />
<router-view v-else v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>

View File

@@ -7,7 +7,8 @@ const api = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
}
},
timeout: 10000 // Timeout de 10 secondes
})
// Interceptor pour ajouter le token JWT

View File

@@ -0,0 +1,126 @@
import { ref } from 'vue'
// Cache global pour les données API
const cache = new Map()
const pendingRequests = new Map()
/**
* Composable pour gérer le cache des requêtes API
* Améliore les performances en évitant les requêtes redondantes
*/
export function useApiCache() {
const getCacheKey = (url, params = {}) => {
return `${url}_${JSON.stringify(params)}`
}
/**
* Récupère des données avec cache
* @param {string} url - URL de l'API
* @param {Function} fetchFn - Fonction pour récupérer les données
* @param {Object} options - Options de cache
* @returns {Promise} - Données en cache ou nouvelles données
*/
const fetchWithCache = async (url, fetchFn, options = {}) => {
const {
ttl = 30000, // Time to live: 30 secondes par défaut
forceRefresh = false,
params = {}
} = options
const cacheKey = getCacheKey(url, params)
// Si pas de rafraîchissement forcé, vérifier le cache
if (!forceRefresh && cache.has(cacheKey)) {
const cached = cache.get(cacheKey)
const now = Date.now()
// Si les données sont encore valides
if (now - cached.timestamp < ttl) {
return cached.data
}
}
// Si une requête est déjà en cours pour cette clé, la réutiliser
if (pendingRequests.has(cacheKey)) {
return pendingRequests.get(cacheKey)
}
// Créer une nouvelle requête
const requestPromise = fetchFn()
.then(data => {
// Mettre en cache
cache.set(cacheKey, {
data,
timestamp: Date.now()
})
// Supprimer de la liste des requêtes en cours
pendingRequests.delete(cacheKey)
return data
})
.catch(error => {
// En cas d'erreur, supprimer de la liste des requêtes en cours
pendingRequests.delete(cacheKey)
throw error
})
// Ajouter à la liste des requêtes en cours
pendingRequests.set(cacheKey, requestPromise)
return requestPromise
}
/**
* Invalide le cache pour une URL spécifique ou tout le cache
* @param {string} url - URL à invalider (optionnel)
*/
const invalidateCache = (url = null) => {
if (url) {
// Invalider toutes les entrées qui commencent par cette URL
for (const key of cache.keys()) {
if (key.startsWith(url)) {
cache.delete(key)
}
}
} else {
// Vider tout le cache
cache.clear()
}
}
/**
* Précharge des données dans le cache
* @param {string} url - URL de l'API
* @param {Function} fetchFn - Fonction pour récupérer les données
*/
const prefetch = async (url, fetchFn, params = {}) => {
const cacheKey = getCacheKey(url, params)
if (!cache.has(cacheKey) && !pendingRequests.has(cacheKey)) {
await fetchWithCache(url, fetchFn, { params })
}
}
/**
* Récupère la taille du cache
*/
const getCacheSize = () => cache.size
/**
* Récupère les statistiques du cache
*/
const getCacheStats = () => ({
size: cache.size,
pendingRequests: pendingRequests.size,
keys: Array.from(cache.keys())
})
return {
fetchWithCache,
invalidateCache,
prefetch,
getCacheSize,
getCacheStats
}
}

View File

@@ -1,49 +1,51 @@
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '../stores/auth'
import LoginView from '../views/LoginView.vue'
import HomelabView from '../views/HomelabView.vue'
import DashboardView from '../views/DashboardView.vue'
import ContainersView from '../views/ContainersView.vue'
import DisksView from '../views/DisksView.vue'
import PackagesView from '../views/PackagesView.vue'
import ShortcutsView from '../views/ShortcutsView.vue'
// Lazy loading pour améliorer les performances
const LoginView = () => import('../views/LoginView.vue')
const HomelabView = () => import('../views/HomelabView.vue')
const DashboardView = () => import('../views/DashboardView.vue')
const ContainersView = () => import('../views/ContainersView.vue')
const DisksView = () => import('../views/DisksView.vue')
const PackagesView = () => import('../views/PackagesView.vue')
const ShortcutsView = () => import('../views/ShortcutsView.vue')
const routes = [
{
path: '/',
name: 'Homelab',
component: HomelabView,
meta: { requiresAuth: false }
meta: { requiresAuth: false, keepAlive: false }
},
{
path: '/login',
name: 'Login',
component: LoginView,
meta: { requiresAuth: false }
meta: { requiresAuth: false, keepAlive: false }
},
{
path: '/dashboard',
name: 'Dashboard',
component: DashboardView,
meta: { requiresAuth: true }
meta: { requiresAuth: true, keepAlive: true }
},
{
path: '/containers',
name: 'Containers',
component: ContainersView,
meta: { requiresAuth: true }
meta: { requiresAuth: true, keepAlive: true }
},
{
path: '/disks',
name: 'Disks',
component: DisksView,
meta: { requiresAuth: true }
meta: { requiresAuth: true, keepAlive: true }
},
{
path: '/packages',
name: 'Packages',
component: PackagesView,
meta: { requiresAuth: true }
meta: { requiresAuth: true, keepAlive: true }
},
{
path: '/shortcuts',

View File

@@ -127,8 +127,11 @@
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ref, onMounted, onActivated, onBeforeUnmount } from 'vue'
import api from '../api'
import { useApiCache } from '../composables/useApiCache'
const { fetchWithCache, invalidateCache } = useApiCache()
const containers = ref([])
const dockerStatus = ref({ connected: false })
@@ -137,12 +140,20 @@ const actionLoading = ref(false)
const showAll = ref(true)
const notification = ref({ show: false, message: '', type: 'success' })
const fetchContainers = async () => {
let refreshInterval = null
const fetchContainers = async (forceRefresh = false) => {
loading.value = true
try {
const response = await api.get('/docker/containers', {
params: { all: showAll.value }
})
const response = await fetchWithCache(
'/docker/containers',
() => api.get('/docker/containers', { params: { all: showAll.value } }),
{
ttl: 5000, // Cache de 5 secondes
forceRefresh,
params: { all: showAll.value }
}
)
containers.value = response.data
} catch (error) {
showNotification('Erreur lors du chargement des conteneurs', 'error')
@@ -154,7 +165,11 @@ const fetchContainers = async () => {
const checkDockerStatus = async () => {
try {
const response = await api.get('/docker/status')
const response = await fetchWithCache(
'/docker/status',
() => api.get('/docker/status'),
{ ttl: 10000 } // Cache de 10 secondes
)
dockerStatus.value = response.data
} catch (error) {
console.error('Erreur statut Docker:', error)
@@ -180,7 +195,10 @@ const actionContainer = async (containerId, action) => {
}
showNotification(`Conteneur ${action}é avec succès`, 'success')
await fetchContainers()
// Invalider le cache et rafraîchir
invalidateCache('/docker/containers')
await fetchContainers(true)
} catch (error) {
showNotification(`Erreur lors du ${action} du conteneur`, 'error')
console.error(error)
@@ -191,7 +209,8 @@ const actionContainer = async (containerId, action) => {
const toggleShowAll = () => {
showAll.value = !showAll.value
fetchContainers()
invalidateCache('/docker/containers')
fetchContainers(true)
}
const getStatusBadgeClass = (state) => {
@@ -208,8 +227,34 @@ const showNotification = (message, type = 'success') => {
}, 3000)
}
// Auto-refresh toutes les 10 secondes quand le composant est actif
const startAutoRefresh = () => {
refreshInterval = setInterval(() => {
fetchContainers()
}, 10000)
}
const stopAutoRefresh = () => {
if (refreshInterval) {
clearInterval(refreshInterval)
refreshInterval = null
}
}
onMounted(() => {
checkDockerStatus()
fetchContainers()
startAutoRefresh()
})
// Appelé quand le composant est réactivé depuis keep-alive
onActivated(() => {
fetchContainers()
startAutoRefresh()
})
// Nettoyage
onBeforeUnmount(() => {
stopAutoRefresh()
})
</script>