Cours JavaScript et React · Module 8: Patterns React avancés
Code splitting
Dans cette leçon14
À mesure que notre application React cosmique grandit, le fichier JavaScript qui se charge dans le navigateur grossit aussi. Cela peut entraîner des temps de chargement plus longs, une utilisation mémoire plus élevée et, de manière générale, de moins bonnes performances. Le code splitting est une technique qui permet de « découper » l'application en plus petites parties pouvant être chargées dynamiquement, uniquement lorsqu'elles sont nécessaires.
Qu'est-ce que le code splitting ?
Le code splitting est une technique qui divise un gros bundle JavaScript en plus petits chunks pouvant être chargés à la demande ou en parallèle. Au lieu d'envoyer toute l'application à l'utilisateur d'un coup, nous n'envoyons que ce qui est nécessaire au rendu initial, et le reste est chargé en arrière-plan ou au besoin.
C'est comme lancer une mission spatiale - nous n'envoyons pas toutes les ressources d'un coup, mais par étapes : d'abord le vaisseau principal, puis les modules supplémentaires, et enfin les outils et équipements plus petits lorsqu'ils deviennent nécessaires.
Pourquoi avons-nous besoin du code splitting ?
Imagine que nous créons une application spatiale avec de nombreux modules :
- Panneau de contrôle de mission
- Système de surveillance des astronautes
- Carte de la galaxie
- Encyclopédie des planètes découvertes
- Simulateur de vol
- Centre de communication interstellaire
Si nous construisons toute l'application en un seul bundle, l'utilisateur doit télécharger tout ce code même s'il veut simplement consulter la carte de la galaxie. En conséquence :
- Temps de chargement initial de la page plus long
- Utilisation mémoire du navigateur plus élevée
- Coûts de transfert de données plus élevés (particulièrement important sur les appareils mobiles)
- Chargement et parsing inutiles de code non utilisé
Avantages du code splitting
- Chargement initial plus rapide - temps d'interactivité plus court (TTI - Time To Interactive)
- Meilleures performances sur les appareils mobiles - utilisation mémoire et CPU plus faible
- Priorisation du chemin critique - les parties les plus importantes de l'application se chargent en premier
- Meilleur caching - les plus petites parties sont plus faciles à mettre en cache et à mettre à jour
- Meilleure expérience utilisateur - l'application devient réactive plus rapidement
Implémenter le code splitting en React
React prend en charge le code splitting via les imports dynamiques et le composant React.lazy. Voyons comment cela fonctionne :
1. Import dynamique
La fonction import() est une fonction ES standard qui permet d'importer dynamiquement des modules. Au lieu de charger toutes les dépendances en haut du fichier, nous pouvons les charger uniquement lorsqu'elles sont nécessaires :
1// Sans code splitting
2import { calculateOrbit } from './complexOrbitCalculations';
3
4function OrbitCalculator({ planetData }) {
5 const orbitInfo = calculateOrbit(planetData);
6 return <div>{/* Rendu des résultats */}</div>;
7}
8
9// Avec code splitting
10function OrbitCalculator({ planetData }) {
11 const [orbitInfo, setOrbitInfo] = useState(null);
12 const [loading, setLoading] = useState(true);
13
14 useEffect(() => {
15 // Import dynamique du module uniquement au montage du composant
16 import('./complexOrbitCalculations')
17 .then(module => {
18 const result = module.calculateOrbit(planetData);
19 setOrbitInfo(result);
20 setLoading(false);
21 });
22 }, [planetData]);
23
24 if (loading) return <p>Calcul de l'orbite...</p>;
25 return <div>{/* Rendu des résultats */}</div>;
26}2. React.lazy
React.lazy est une fonction qui permet de rendre un import dynamique comme un composant ordinaire. Elle ne fonctionne qu'avec les exports par défaut (default).
1import React, { Suspense } from 'react';
2
3// Import classique
4// import GalaxyMap from './GalaxyMap';
5
6// Import lazy
7const GalaxyMap = React.lazy(() => import('./GalaxyMap'));
8const PlanetDetails = React.lazy(() => import('./PlanetDetails'));
9const SpaceshipControls = React.lazy(() => import('./SpaceshipControls'));
10
11function SpaceExplorer() {
12 const [view, setView] = useState('map');
13
14 return (
15 <div className="space-explorer">
16 <nav>
17 <button onClick={() => setView('map')}>Carte de la galaxie</button>
18 <button onClick={() => setView('planets')}>Détails des planètes</button>
19 <button onClick={() => setView('controls')}>Panneau de contrôle</button>
20 </nav>
21
22 <Suspense fallback={<div>Chargement du module...</div>}>
23 {view === 'map' && <GalaxyMap />}
24 {view === 'planets' && <PlanetDetails />}
25 {view === 'controls' && <SpaceshipControls />}
26 </Suspense>
27 </div>
28 );
29}Dans l'exemple ci-dessus :
- Nous utilisons
React.lazypour importer dynamiquement les composants - Nous enveloppons les composants dans
Suspensepour afficher un fallback pendant le chargement - Les composants ne sont chargés que lorsqu'ils sont nécessaires (quand l'utilisateur change de vue)
Suspense
Suspense est un composant React qui permet de « suspendre » le rendu jusqu'à ce que certaines conditions soient remplies (par exemple, données chargées ou code du composant disponible).
Suspense accepte une prop fallback, qui est rendue pendant l'attente :
1<Suspense fallback={<LoadingSpinner message="Préparation du module..." />}>
2 <LazilyLoadedComponent />
3</Suspense>Suspense peut gérer plusieurs composants Lazy à la fois :
1<Suspense fallback={<LoadingSpinner />}>
2 <LazyComponent1 />
3 <div>
4 <LazyComponent2 />
5 </div>
6 <LazyComponent3 />
7</Suspense>Imbriquer Suspense
Nous pouvons aussi imbriquer les composants Suspense pour obtenir un contrôle plus granulaire sur ce qui est affiché pendant le chargement :
1function SpaceStation() {
2 return (
3 <div>
4 <h1>Station spatiale Alpha</h1>
5
6 {/* Le layout principal se charge immédiatement */}
7 <StationLayout />
8
9 {/* La carte se charge avec son propre fallback */}
10 <Suspense fallback={<p>Chargement de la carte de la station...</p>}>
11 <StationMap />
12 </Suspense>
13
14 {/* Le panneau de contrôle a des composants lazy imbriqués */}
15 <Suspense fallback={<p>Préparation du panneau de contrôle...</p>}>
16 <ControlPanel>
17 <Suspense fallback={<p>Chargement des systèmes de support vital...</p>}>
18 <LifeSupportMonitor />
19 </Suspense>
20
21 <Suspense fallback={<p>Chargement des commandes des moteurs...</p>}>
22 <EngineControls />
23 </Suspense>
24 </ControlPanel>
25 </Suspense>
26 </div>
27 );
28}Code splitting basé sur les routes
L'une des manières les plus courantes d'appliquer le code splitting est de diviser le code en fonction des routes. Chaque page ou vue devient un chunk de code distinct :
1import React, { Suspense, lazy } from 'react';
2import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
3import Navbar from './Navbar';
4import LoadingPage from './LoadingPage';
5
6// Lazy loading des pages
7const MissionControl = lazy(() => import('./pages/MissionControl'));
8const GalaxyMap = lazy(() => import('./pages/GalaxyMap'));
9const CrewManagement = lazy(() => import('./pages/CrewManagement'));
10const ResearchDatabase = lazy(() => import('./pages/ResearchDatabase'));
11const SpaceshipConfig = lazy(() => import('./pages/SpaceshipConfig'));
12
13function App() {
14 return (
15 <Router>
16 <Navbar />
17 <Suspense fallback={<LoadingPage />}>
18 <Switch>
19 <Route exact path="/" component={MissionControl} />
20 <Route path="/galaxy-map" component={GalaxyMap} />
21 <Route path="/crew" component={CrewManagement} />
22 <Route path="/research" component={ResearchDatabase} />
23 <Route path="/spaceship" component={SpaceshipConfig} />
24 </Switch>
25 </Suspense>
26 </Router>
27 );
28}Code splitting des bibliothèques
Nous pouvons aussi appliquer le code splitting à de grandes bibliothèques qui ne sont pas nécessaires immédiatement :
1import React, { useState } from 'react';
2
3function DataVisualizer({ data }) {
4 const [charts, setCharts] = useState(null);
5 const [showCharts, setShowCharts] = useState(false);
6
7 const loadChartsLibrary = async () => {
8 // Charger la bibliothèque uniquement quand l'utilisateur veut voir les graphiques
9 const D3Module = await import('d3');
10 const ChartModule = await import('./ChartComponent');
11
12 setCharts({
13 d3: D3Module.default,
14 ChartComponent: ChartModule.default
15 });
16 };
17
18 const handleShowCharts = () => {
19 if (!charts) {
20 setShowCharts(true);
21 loadChartsLibrary();
22 } else {
23 setShowCharts(true);
24 }
25 };
26
27 return (
28 <div className="data-visualizer">
29 <h2>Données d'exploration spatiale</h2>
30 <button onClick={handleShowCharts}>
31 {showCharts ? 'Masquer les graphiques' : 'Afficher les graphiques'}
32 </button>
33
34 {showCharts && (
35 <>
36 {charts ? (
37 <charts.ChartComponent data={data} d3={charts.d3} />
38 ) : (
39 <p>Chargement des graphiques avancés...</p>
40 )}
41 </>
42 )}
43 </div>
44 );
45}Preloading
Parfois nous savons qu'un composant sera bientôt nécessaire (par exemple, quand l'utilisateur survole un bouton qui affichera une modale). Nous pouvons commencer à charger le module plus tôt :
1const PlanetExplorer = React.lazy(() => import('./PlanetExplorer'));
2
3function PlanetInfoButton({ planetId }) {
4 const [showExplorer, setShowExplorer] = useState(false);
5
6 const handleMouseEnter = () => {
7 // Commencer à télécharger le module quand l'utilisateur survole le bouton
8 const PlanetExplorerModule = import('./PlanetExplorer');
9 };
10
11 return (
12 <>
13 <button
14 onMouseEnter={handleMouseEnter}
15 onClick={() => setShowExplorer(true)}
16 >
17 Explorer la planète {planetId}
18 </button>
19
20 {showExplorer && (
21 <Suspense fallback={<p>Préparation de l'explorateur...</p>}>
22 <PlanetExplorer planetId={planetId} />
23 </Suspense>
24 )}
25 </>
26 );
27}Named exports et code splitting
React.lazy ne fonctionne qu'avec les exports par défaut. Si nous voulons utiliser des named exports, nous devons créer un module intermédiaire :
1// MathUtils.js
2export const calculateDistance = (a, b) => { /* ... */ };
3export const calculateVelocity = (distance, time) => { /* ... */ };
4export const calculateAcceleration = (velocity, time) => { /* ... */ };
5
6// DistanceCalculator.js (module intermédiaire)
7export { calculateDistance } from './MathUtils';
8export default calculateDistance;
9
10// Utilisation
11const DistanceCalculator = React.lazy(() => import('./DistanceCalculator'));Alternativement, nous pouvons gérer l'import manuellement et extraire l'export souhaité :
1const calculateVelocity = React.lazy(() =>
2 import('./MathUtils').then(module => ({ default: module.calculateVelocity }))
3);Stratégies de code splitting
1. Découpage basé sur les routes
L'approche la plus courante et la plus simple - chaque page est un chunk de code distinct.
2. Découpage basé sur les fonctionnalités
Les composants liés à une fonctionnalité spécifique (par exemple, panneau d'administration, analytique avancée) sont regroupés ensemble.
1// Chargement de tout le module admin à la demande
2const AdminDashboard = React.lazy(() => import('./admin/Dashboard'));
3
4function App() {
5 const [isAdmin, setIsAdmin] = useState(false);
6
7 // isAdmin est défini après la connexion de l'utilisateur
8
9 return (
10 <div>
11 {isAdmin ? (
12 <Suspense fallback={<p>Chargement du panneau admin...</p>}>
13 <AdminDashboard />
14 </Suspense>
15 ) : (
16 <UserDashboard />
17 )}
18 </div>
19 );
20}3. Découpage basé sur la priorité
Composants critiques chargés immédiatement, moins importants chargés en lazy.
1// Composants critiques importés normalement
2import CriticalSystemStatus from './CriticalSystemStatus';
3import NavigationControls from './NavigationControls';
4
5// Composants moins importants chargés en lazy
6const SystemLogs = React.lazy(() => import('./SystemLogs'));
7const CrewSchedule = React.lazy(() => import('./CrewSchedule'));
8const MaintenanceStatus = React.lazy(() => import('./MaintenanceStatus'));
9
10function SpaceshipDashboard() {
11 return (
12 <div className="dashboard">
13 <div className="critical-section">
14 <CriticalSystemStatus />
15 <NavigationControls />
16 </div>
17
18 <Suspense fallback={<p>Chargement des modules supplémentaires...</p>}>
19 <div className="secondary-section">
20 <SystemLogs />
21 <CrewSchedule />
22 <MaintenanceStatus />
23 </div>
24 </Suspense>
25 </div>
26 );
27}4. Vendor splitting
Découpage de grandes bibliothèques externes en chunks distincts.
Dans webpack, cela peut être réalisé grâce à la configuration splitChunks :
1module.exports = {
2 //...
3 optimization: {
4 splitChunks: {
5 chunks: 'all',
6 maxInitialRequests: Infinity,
7 minSize: 0,
8 cacheGroups: {
9 vendor: {
10 test: /[\/]node_modules[\/]/,
11 name(module) {
12 // get the name. E.g. node_modules/packageName/sub/path
13 // or node_modules/packageName
14 const packageName = module.context.match(
15 /[\/]node_modules[\/](.*?)([\/]|$)/
16 )[1];
17
18 // npm package names are URL-safe, but some servers don't like @ symbols
19 return `npm.${packageName.replace('@', '')}`;
20 },
21 },
22 },
23 },
24 },
25};Analyser et déboguer le code splitting
Analyser la taille des bundles
Des outils comme « webpack-bundle-analyzer » ou « source-map-explorer » aident à visualiser le contenu des bundles et à identifier les opportunités de découpage :
1# Installation
2npm install --save-dev webpack-bundle-analyzer
3
4# Configuration dans webpack.config.js
5const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
6
7module.exports = {
8 plugins: [
9 new BundleAnalyzerPlugin()
10 ]
11}Déboguer les problèmes de code splitting
Problèmes courants de code splitting et leurs solutions :
Les composants clignotent pendant le chargement
- Solution : Conçois le fallback pour qu'il corresponde au layout final du composant
Trop de petits chunks
- Solution : Regroupe les composants liés dans un seul import dynamique
Problèmes de SSR (Server-Side Rendering)
- Solution : Utilise
loadable-componentsau lieu deReact.lazypour le SSR
- Solution : Utilise
Bonnes pratiques de code splitting
N'abuse pas du découpage - trop de petits chunks peut entraîner des problèmes de performance dus aux nombreuses requêtes HTTP
Analyse tes bundles - surveille régulièrement la taille et le contenu des bundles
Utilise les Magic Comments de webpack pour un meilleur contrôle des chunks :
1// Named chunk 2const HomeComponent = React.lazy(() => import( 3 /* webpackChunkName: "home" */ 4 './HomeComponent' 5)); 6 7// Prefetch - suggère au navigateur que cette ressource sera nécessaire 8const SettingsComponent = React.lazy(() => import( 9 /* webpackChunkName: "settings" */ 10 /* webpackPrefetch: true */ 11 './SettingsComponent' 12)); 13 14// Preload - charge cette ressource avec une haute priorité 15const ProfileComponent = React.lazy(() => import( 16 /* webpackChunkName: "profile" */ 17 /* webpackPreload: true */ 18 './ProfileComponent' 19));Utilise le code splitting avec le caching - des en-têtes de cache bien configurés pour les chunks peuvent considérablement améliorer les performances
Surveille l'impact sur les performances - utilise des outils comme Lighthouse pour mesurer les temps de chargement et le TTI (Time To Interactive)
Code splitting et TypeScript
TypeScript fonctionne bien avec le code splitting mais nécessite quelques étapes supplémentaires :
1// Assure-toi que tsconfig.json contient :
2// "module": "esnext"
3
4// Import dynamique avec typage
5const DataVisualization = React.lazy(() =>
6 import('./DataVisualization') as Promise<{ default: React.ComponentType<DataVisProps> }>
7);
8
9interface DataVisProps {
10 data: Array<DataPoint>;
11 width: number;
12 height: number;
13}
14
15interface DataPoint {
16 x: number;
17 y: number;
18 label: string;
19}
20
21function Dashboard() {
22 const [data, setData] = useState<Array<DataPoint>>([]);
23
24 return (
25 <div>
26 <h1>Panneau de mission spatiale</h1>
27
28 <Suspense fallback={<div>Chargement de la visualisation...</div>}>
29 <DataVisualization
30 data={data}
31 width={800}
32 height={400}
33 />
34 </Suspense>
35 </div>
36 );
37}Résumé
Le code splitting est une puissante technique d'optimisation qui permet d'améliorer considérablement les performances des applications React, surtout pour les projets plus importants. Grâce au code splitting, les utilisateurs ne chargent que ce dont ils ont besoin, ce qui conduit à un démarrage plus rapide de l'application et à une meilleure expérience utilisateur.
Les outils et méthodes clés incluent :
React.lazy()etSuspensepour les composantsimport()dynamique pour les modules- Stratégies de découpage basées sur les routes, les fonctionnalités ou la priorité
- Outils d'analyse de bundles pour aider à la prise de décision
En implémentant le code splitting dans notre application cosmique, nous pouvons garantir que les utilisateurs accèdent rapidement aux fonctionnalités les plus importantes, et que les ressources supplémentaires sont chargées lorsqu'elles sont nécessaires - à l'image d'une mission spatiale à plusieurs étapes où chaque module est livré au bon moment.
Code de cette leçon : App.jsx
1import React, { createContext, useContext, useState } from 'react';
2
3// ============================================
4// PATTERN : Compound Components
5// ============================================
6// Composants partageant l'etat via le Context
7
8// Context pour Accordion
9const AccordionContext = createContext();
10
11function useAccordion() {
12 const context = useContext(AccordionContext);
13 if (!context) {
14 throw new Error('Accordion components must be used within <Accordion>');
15 }
16 return context;
17}
18
19// Composant Accordion principal
20function Accordion({ children, defaultOpen = null }) {
21 const [openItem, setOpenItem] = useState(defaultOpen);
22
23 const toggle = (id) => {
24 setOpenItem(prev => prev === id ? null : id);
25 };
26
27 return (
28 <AccordionContext.Provider value={{ openItem, toggle }}>
29 <div style={styles.accordion}>
30 {children}
31 </div>
32 </AccordionContext.Provider>
33 );
34}
35
36// Sous-composant : Item
37function AccordionItem({ children, id }) {
38 const { openItem } = useAccordion();
39 const isOpen = openItem === id;
40
41 return (
42 <div style={{
43 ...styles.item,
44 borderColor: isOpen ? '#00d4ff' : 'rgba(255,255,255,0.1)',
45 }}>
46 {React.Children.map(children, child =>
47 React.cloneElement(child, { itemId: id, isOpen })
48 )}
49 </div>
50 );
51}
52
53// Sous-composant : Header
54function AccordionHeader({ children, itemId }) {
55 const { openItem, toggle } = useAccordion();
56 const isOpen = openItem === itemId;
57
58 return (
59 <button
60 onClick={() => toggle(itemId)}
61 style={styles.header}
62 >
63 <span>{children}</span>
64 <span style={{
65 transform: isOpen ? 'rotate(180deg)' : 'rotate(0)',
66 transition: 'transform 0.3s ease',
67 }}>
68 ▼
69 </span>
70 </button>
71 );
72}
73
74// Sous-composant : Panel
75function AccordionPanel({ children, isOpen }) {
76 return (
77 <div style={{
78 ...styles.panel,
79 maxHeight: isOpen ? '500px' : '0',
80 padding: isOpen ? '20px' : '0 20px',
81 opacity: isOpen ? 1 : 0,
82 }}>
83 {children}
84 </div>
85 );
86}
87
88// Nous assemblons les composants
89Accordion.Item = AccordionItem;
90Accordion.Header = AccordionHeader;
91Accordion.Panel = AccordionPanel;
92
93// ============================================
94// PATTERN : Tabs Compound Component
95// ============================================
96
97const TabsContext = createContext();
98
99function Tabs({ children, defaultTab }) {
100 const [activeTab, setActiveTab] = useState(defaultTab);
101
102 return (
103 <TabsContext.Provider value={{ activeTab, setActiveTab }}>
104 <div style={styles.tabs}>
105 {children}
106 </div>
107 </TabsContext.Provider>
108 );
109}
110
111function TabList({ children }) {
112 return (
113 <div style={styles.tabList}>
114 {children}
115 </div>
116 );
117}
118
119function Tab({ children, id }) {
120 const { activeTab, setActiveTab } = useContext(TabsContext);
121 const isActive = activeTab === id;
122
123 return (
124 <button
125 onClick={() => setActiveTab(id)}
126 style={{
127 ...styles.tab,
128 background: isActive ? '#00d4ff' : 'transparent',
129 color: isActive ? '#000' : '#fff',
130 }}
131 >
132 {children}
133 </button>
134 );
135}
136
137function TabPanels({ children }) {
138 return <div>{children}</div>;
139}
140
141function TabPanel({ children, id }) {
142 const { activeTab } = useContext(TabsContext);
143
144 if (activeTab !== id) return null;
145
146 return (
147 <div style={styles.tabPanel}>
148 {children}
149 </div>
150 );
151}
152
153Tabs.List = TabList;
154Tabs.Tab = Tab;
155Tabs.Panels = TabPanels;
156Tabs.Panel = TabPanel;
157
158// ============================================
159// App de demo
160// ============================================
161
162function CompoundComponentsDemo() {
163 return (
164 <div style={styles.container}>
165 <header style={styles.headerSection}>
166 <h1 style={styles.title}>Compound Components</h1>
167 <p style={styles.subtitle}>
168 Design pattern for components sharing state
169 </p>
170 </header>
171
172 <div style={styles.content}>
173 {/* Accordion Demo */}
174 <div style={styles.section}>
175 <h2 style={styles.sectionTitle}>Accordion</h2>
176
177 <Accordion defaultOpen="item1">
178 <Accordion.Item id="item1">
179 <Accordion.Header>What are Compound Components?</Accordion.Header>
180 <Accordion.Panel>
181 <p>
182 Compound Components is a pattern where components share
183 internal state through Context. This allows creating
184 flexible and readable component APIs.
185 </p>
186 </Accordion.Panel>
187 </Accordion.Item>
188
189 <Accordion.Item id="item2">
190 <Accordion.Header>Avantages du pattern</Accordion.Header>
191 <Accordion.Panel>
192 <ul style={{ marginLeft: '20px' }}>
193 <li>Readable and declarative syntax</li>
194 <li>Flexibility in component layout</li>
195 <li>Encapsulation de la logique dans le parent</li>
196 <li>Easy addition of new features</li>
197 </ul>
198 </Accordion.Panel>
199 </Accordion.Item>
200
201 <Accordion.Item id="item3">
202 <Accordion.Header>Implémentation</Accordion.Header>
203 <Accordion.Panel>
204 <p>
205 Implementation relies on React Context for sharing
206 state and on static properties for grouping sub-components.
207 </p>
208 </Accordion.Panel>
209 </Accordion.Item>
210 </Accordion>
211 </div>
212
213 {/* Tabs Demo */}
214 <div style={styles.section}>
215 <h2 style={styles.sectionTitle}>Tabs</h2>
216
217 <Tabs defaultTab="tab1">
218 <Tabs.List>
219 <Tabs.Tab id="tab1">Planètes</Tabs.Tab>
220 <Tabs.Tab id="tab2">Étoiles</Tabs.Tab>
221 <Tabs.Tab id="tab3">Galaxies</Tabs.Tab>
222 </Tabs.List>
223
224 <Tabs.Panels>
225 <Tabs.Panel id="tab1">
226 <h3>Planets of the Solar System</h3>
227 <p>Mercure, Vénus, Terre, Mars, Jupiter, Saturne, Uranus, Neptune</p>
228 </Tabs.Panel>
229 <Tabs.Panel id="tab2">
230 <h3>Types d'étoiles</h3>
231 <p>Stars are classified into dwarfs, giants, supergiants and other spectral types.</p>
232 </Tabs.Panel>
233 <Tabs.Panel id="tab3">
234 <h3>Types de galaxies</h3>
235 <p>Galaxies spirales, elliptiques, irrégulières et lenticulaires.</p>
236 </Tabs.Panel>
237 </Tabs.Panels>
238 </Tabs>
239 </div>
240 </div>
241
242 <footer style={styles.footer}>
243 <p>Compound Components • Context API • Static Properties</p>
244 </footer>
245 </div>
246 );
247}
248
249const styles = {
250 container: {
251 minHeight: '100vh',
252 background: 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)',
253 padding: '30px',
254 fontFamily: "'Segoe UI', sans-serif",
255 color: '#fff',
256 },
257 headerSection: {
258 textAlign: 'center',
259 marginBottom: '40px',
260 },
261 title: {
262 fontSize: '2.5em',
263 color: '#00d4ff',
264 marginBottom: '10px',
265 },
266 subtitle: {
267 opacity: 0.7,
268 },
269 content: {
270 maxWidth: '800px',
271 margin: '0 auto',
272 },
273 section: {
274 background: 'rgba(255,255,255,0.05)',
275 borderRadius: '15px',
276 padding: '25px',
277 marginBottom: '25px',
278 border: '1px solid rgba(255,255,255,0.1)',
279 },
280 sectionTitle: {
281 color: '#ffd700',
282 marginBottom: '20px',
283 },
284 accordion: {
285 display: 'flex',
286 flexDirection: 'column',
287 gap: '10px',
288 },
289 item: {
290 borderRadius: '10px',
291 border: '2px solid',
292 overflow: 'hidden',
293 transition: 'border-color 0.3s ease',
294 },
295 header: {
296 width: '100%',
297 padding: '15px 20px',
298 background: 'rgba(255,255,255,0.05)',
299 border: 'none',
300 color: '#fff',
301 fontSize: '16px',
302 cursor: 'pointer',
303 display: 'flex',
304 justifyContent: 'space-between',
305 alignItems: 'center',
306 },
307 panel: {
308 overflow: 'hidden',
309 transition: 'all 0.3s ease',
310 lineHeight: 1.6,
311 },
312 tabs: {},
313 tabList: {
314 display: 'flex',
315 gap: '10px',
316 marginBottom: '20px',
317 },
318 tab: {
319 padding: '12px 24px',
320 border: '2px solid #00d4ff',
321 borderRadius: '8px',
322 cursor: 'pointer',
323 fontWeight: 'bold',
324 transition: 'all 0.3s ease',
325 },
326 tabPanel: {
327 padding: '20px',
328 background: 'rgba(255,255,255,0.05)',
329 borderRadius: '10px',
330 },
331 footer: {
332 textAlign: 'center',
333 marginTop: '40px',
334 opacity: 0.7,
335 },
336};
337
338export default CompoundComponentsDemo;Tu as repéré une erreur dans cette leçon ?
Tâches pratiques dans le jeu
- Clique dans l'ordre
Ordonne la syntaxe de React.lazy avec un import dynamique :