fix nginx
This commit is contained in:
parent
0afde29e22
commit
1e73530afa
19
index.html
19
index.html
@ -7,7 +7,7 @@
|
|||||||
<!-- DNS Prefetch & Preconnect pour les domaines tiers -->
|
<!-- DNS Prefetch & Preconnect pour les domaines tiers -->
|
||||||
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
|
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
|
||||||
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
|
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
|
||||||
<!-- GTM sera chargé dynamiquement après consentement -->
|
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
|
||||||
@ -33,14 +33,25 @@
|
|||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
<!-- Google Tag Manager sera chargé dynamiquement après consentement pour optimiser LCP/FCP -->
|
<!-- Google Tag Manager - Chargé de manière asynchrone -->
|
||||||
<!-- Voir src/utils/gtm.ts et src/main.tsx pour l'implémentation -->
|
<script>
|
||||||
|
// Defer GTM loading to improve initial page load
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||||
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||||
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||||
|
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||||
|
})(window,document,'script','dataLayer','GTM-5V6TCG4C');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<!-- End Google Tag Manager -->
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- Google Tag Manager (noscript) - Chargé uniquement sans JavaScript -->
|
<!-- Google Tag Manager (noscript) -->
|
||||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5V6TCG4C"
|
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5V6TCG4C"
|
||||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||||
|
<!-- End Google Tag Manager (noscript) -->
|
||||||
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
|||||||
@ -11,8 +11,7 @@
|
|||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"optimize-images": "node scripts/optimize-images.js",
|
"optimize-images": "node scripts/optimize-images.js",
|
||||||
"analyze": "node scripts/analyze-bundle.js",
|
"analyze": "node scripts/analyze-bundle.js"
|
||||||
"analyze:js": "node scripts/analyze-js-bundle.js"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^3.10.0",
|
"@hookform/resolvers": "^3.10.0",
|
||||||
|
|||||||
23
src/App.tsx
23
src/App.tsx
@ -6,30 +6,15 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
|||||||
import { lazy, Suspense } from "react";
|
import { lazy, Suspense } from "react";
|
||||||
import { ThemeProvider } from "./contexts/ThemeContext";
|
import { ThemeProvider } from "./contexts/ThemeContext";
|
||||||
|
|
||||||
// Lazy load pages et composants lourds pour de meilleures performances
|
// Lazy load pages and heavy components for better performance
|
||||||
// Cela réduit la quantité de JavaScript chargé initialement
|
|
||||||
const Index = lazy(() => import("./pages/Index"));
|
const Index = lazy(() => import("./pages/Index"));
|
||||||
const ProjectPage = lazy(() => import("./pages/ProjectPage"));
|
const ProjectPage = lazy(() => import("./pages/ProjectPage"));
|
||||||
const NotFound = lazy(() => import("./pages/NotFound"));
|
const NotFound = lazy(() => import("./pages/NotFound"));
|
||||||
|
const ParticlesBackground = lazy(() => import("./components/ParticlesBackground").then(m => ({ default: m.ParticlesBackground })));
|
||||||
|
|
||||||
// ParticlesBackground est chargé en lazy car non critique pour le FCP/LCP
|
const queryClient = new QueryClient();
|
||||||
const ParticlesBackground = lazy(() =>
|
|
||||||
import("./components/ParticlesBackground").then(m => ({ default: m.ParticlesBackground }))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Configuration QueryClient optimisée
|
// Loading fallback component
|
||||||
const queryClient = new QueryClient({
|
|
||||||
defaultOptions: {
|
|
||||||
queries: {
|
|
||||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
||||||
gcTime: 1000 * 60 * 10, // 10 minutes
|
|
||||||
refetchOnWindowFocus: false,
|
|
||||||
retry: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Loading fallback component minimal
|
|
||||||
const PageLoader = () => (
|
const PageLoader = () => (
|
||||||
<div className="min-h-screen flex items-center justify-center">
|
<div className="min-h-screen flex items-center justify-center">
|
||||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
|
||||||
|
|||||||
@ -27,16 +27,16 @@ export const ProjectCard = ({ title, description, icon, image, technologies, del
|
|||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ duration: 0.2, delay }}
|
transition={{ duration: 0.5, delay }}
|
||||||
whileHover={{ y: -8, transition: { duration: 0.2 } }}
|
whileHover={{ y: -5 }}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className={projectId ? "cursor-pointer" : ""}
|
className={projectId ? "cursor-pointer" : ""}
|
||||||
>
|
>
|
||||||
<Card className="h-full hover:shadow-xl transition-all duration-200 ease-out border-border/50 bg-card/50 backdrop-blur relative overflow-hidden group">
|
<Card className="h-full hover:shadow-lg transition-all duration-300 border-border/50 bg-card/50 backdrop-blur relative overflow-hidden group">
|
||||||
{/* Indicateur cliquable en bas à droite */}
|
{/* Indicateur cliquable en bas à droite */}
|
||||||
{projectId && (
|
{projectId && (
|
||||||
<div className="absolute bottom-4 right-4 w-8 h-8 rounded-full bg-primary/10 group-hover:bg-primary/20 flex items-center justify-center transition-all duration-200 ease-out group-hover:scale-125">
|
<div className="absolute bottom-4 right-4 w-8 h-8 rounded-full bg-primary/10 group-hover:bg-primary/20 flex items-center justify-center transition-all duration-300 group-hover:scale-110">
|
||||||
<ArrowRight className="w-4 h-4 text-primary group-hover:translate-x-1 transition-transform duration-200 ease-out" />
|
<ArrowRight className="w-4 h-4 text-primary group-hover:translate-x-0.5 transition-transform duration-300" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -30,60 +30,17 @@ export default defineConfig(({ mode }) => ({
|
|||||||
// Chunking optimal pour de meilleures performances
|
// Chunking optimal pour de meilleures performances
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
// Split vendors pour améliorer le cache et réduire le code inutilisé
|
// Split vendors pour améliorer le cache
|
||||||
manualChunks: (id) => {
|
manualChunks: {
|
||||||
// Ignorer les node_modules non critiques
|
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
|
||||||
if (id.includes('node_modules')) {
|
'ui-vendor': ['framer-motion', 'lucide-react'],
|
||||||
// React core - bundle séparé pour un meilleur cache
|
'particles': ['@tsparticles/react', '@tsparticles/slim', '@tsparticles/engine'],
|
||||||
if (id.includes('/react/') || id.includes('/react-dom/') || id.includes('/scheduler/')) {
|
|
||||||
return 'react-core';
|
|
||||||
}
|
|
||||||
|
|
||||||
// React Router - souvent utilisé
|
|
||||||
if (id.includes('/react-router-dom/') || id.includes('/@remix-run/')) {
|
|
||||||
return 'react-router';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Radix UI - grouper tous les composants ensemble avec tree-shaking
|
|
||||||
if (id.includes('@radix-ui/')) {
|
|
||||||
return 'radix-ui';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Framer Motion - animations (peut être volumineux)
|
|
||||||
if (id.includes('/framer-motion/')) {
|
|
||||||
return 'animations';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lucide React - icônes (volumineux)
|
|
||||||
if (id.includes('/lucide-react/')) {
|
|
||||||
return 'icons';
|
|
||||||
}
|
|
||||||
|
|
||||||
// tsparticles - animations de fond (optionnel)
|
|
||||||
if (id.includes('@tsparticles/') || id.includes('/tsparticles/')) {
|
|
||||||
return 'particles';
|
|
||||||
}
|
|
||||||
|
|
||||||
// TanStack Query
|
|
||||||
if (id.includes('@tanstack/')) {
|
|
||||||
return 'tanstack';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Autres dépendances moins critiques
|
|
||||||
return 'vendor';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// Nommer les chunks de manière cohérente pour le cache
|
// Nommer les chunks de manière cohérente pour le cache
|
||||||
chunkFileNames: 'assets/js/[name]-[hash].js',
|
chunkFileNames: 'assets/js/[name]-[hash].js',
|
||||||
entryFileNames: 'assets/js/[name]-[hash].js',
|
entryFileNames: 'assets/js/[name]-[hash].js',
|
||||||
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
|
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
|
||||||
},
|
},
|
||||||
// Optimisations supplémentaires pour le tree-shaking
|
|
||||||
treeshake: {
|
|
||||||
moduleSideEffects: 'no-external', // Pas d'effets de bord pour les modules externes
|
|
||||||
propertyReadSideEffects: false,
|
|
||||||
unknownGlobalSideEffects: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// Optimisation des assets
|
// Optimisation des assets
|
||||||
assetsInlineLimit: 4096, // Images < 4kb seront inline en base64
|
assetsInlineLimit: 4096, // Images < 4kb seront inline en base64
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user