Que fait Développement Frontend Senior ?
Modèles de développement frontend, optimisation des performances et outils d'automatisation pour les applications React/Next.js.
Table des matières
- Scaffolding de projet
- Génération de composants
- Analyse du bundle
- Modèles React
- Optimisation Next.js
- Accessibilité et tests
Scaffolding de projet
Générer un nouveau projet Next.js ou React avec TypeScript, Tailwind CSS et des configurations suivant les meilleures pratiques.
Workflow : Créer un nouveau projet frontend
-
Exécutez le script de scaffolding avec le nom et le modèle de votre projet :
python scripts/frontend_scaffolder.py mon-app --template nextjs -
Ajoutez des fonctionnalités optionnelles (auth, api, formulaires, tests, storybook) :
python scripts/frontend_scaffolder.py tableau-de-bord --template nextjs --features auth,api -
Accédez au projet et installez les dépendances :
cd mon-app && npm install -
Lancez le serveur de développement :
npm run dev
Options du script de scaffolding
| Option | Description |
|---|---|
--template nextjs | Next.js 14+ avec App Router et Server Components |
--template react | React + Vite avec TypeScript |
--features auth | Ajouter l'authentification NextAuth.js |
--features api | Ajouter React Query + client API |
--features forms | Ajouter React Hook Form + validation Zod |
--features testing | Ajouter Vitest + Testing Library |
--dry-run | Prévisualiser les fichiers sans les créer |
Structure générée (Next.js)
mon-app/
├── app/
│ ├── layout.tsx # Layout racine avec polices
│ ├── page.tsx # Page d'accueil
│ ├── globals.css # Tailwind + variables CSS
│ └── api/health/route.ts
├── components/
│ ├── ui/ # Button, Input, Card
│ └── layout/ # Header, Footer, Sidebar
├── hooks/ # useDebounce, useLocalStorage
├── lib/ # utils (cn), constantes
├── types/ # Interfaces TypeScript
├── tailwind.config.ts
├── next.config.js
└── package.json
Génération de composants
Générer des composants React avec TypeScript, des tests et des stories Storybook.
Workflow : Créer un nouveau composant
-
Générez un composant client :
python scripts/component_generator.py Bouton --dir src/components/ui -
Générez un composant serveur :
python scripts/component_generator.py CarteProduit --type server -
Générez avec des fichiers de test et de story :
python scripts/component_generator.py ProfilUtilisateur --with-test --with-story -
Générez un hook personnalisé :
python scripts/component_generator.py ValidationFormulaire --type hook
Options du générateur
| Option | Description |
|---|---|
--type client | Composant client avec 'use client' (par défaut) |
--type server | Composant serveur asynchrone |
--type hook | Hook React personnalisé |
--with-test | Inclure un fichier de test |
--with-story | Inclure une story Storybook |
--flat | Créer dans le répertoire de sortie sans sous-répertoire |
--dry-run | Prévisualiser sans créer de fichiers |
Exemple de composant généré
'use client';
import { useState } from 'react';
import { cn } from '@/lib/utils';
interface ButtonProps {
className?: string;
children?: React.ReactNode;
}
export function Button({ className, children }: ButtonProps) {
return (
<div className={cn('', className)}>
{children}
</div>
);
}
Analyse du bundle
Analyser le fichier package.json et la structure du projet pour identifier les opportunités d'optimisation du bundle.
Workflow : Optimiser la taille du bundle
-
Exécutez l'analyseur sur votre projet :
python scripts/bundle_analyzer.py /chemin/vers/projet -
Consultez le score de santé et les problèmes :
Score de santé du bundle : 75/100 (C) DÉPENDANCES LOURDES : moment (290 Ko) Alternative : date-fns (12 Ko) ou dayjs (2 Ko) lodash (71 Ko) Alternative : lodash-es avec tree-shaking -
Appliquez les correctifs recommandés en remplaçant les dépendances lourdes.
-
Réexécutez en mode verbeux pour vérifier les patterns d'import :
python scripts/bundle_analyzer.py . --verbose
Interprétation du score du bundle
| Score | Note | Action |
|---|---|---|
| 90-100 | A | Le bundle est bien optimisé |
| 80-89 | B | Optimisations mineures disponibles |
| 70-79 | C | Remplacer les dépendances lourdes |
| 60-69 | D | Plusieurs problèmes nécessitent une attention |
| 0-59 | F | Problèmes critiques de taille du bundle |
Dépendances lourdes détectées
L'analyseur identifie ces packages lourds courants :
| Package | Taille | Alternative |
|---|---|---|
| moment | 290 Ko | date-fns (12 Ko) ou dayjs (2 Ko) |
| lodash | 71 Ko | lodash-es avec tree-shaking |
| axios | 14 Ko | Fetch natif ou ky (3 Ko) |
| jquery | 87 Ko | API DOM natives |
| @mui/material | Grande | shadcn/ui ou Radix UI |
Modèles React
Référence : references/react_patterns.md
Composants composés
Partager l'état entre des composants liés :
const Tabs = ({ children }) => {
const [active, setActive] = useState(0);
return (
<TabsContext.Provider value={{ active, setActive }}>
{children}
</TabsContext.Provider>
);
};
Tabs.List = TabList;
Tabs.Panel = TabPanel;
// Utilisation
<Tabs>
<Tabs.List>
<Tabs.Tab>Un</Tabs.Tab>
<Tabs.Tab>Deux</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>Contenu 1</Tabs.Panel>
<Tabs.Panel>Contenu 2</Tabs.Panel>
</Tabs>
Hooks personnalisés
Extraire la logique réutilisable :
function useDebounce<T>(value: T, delay = 500): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Utilisation
const debouncedSearch = useDebounce(termeRecherche, 300);
Props de rendu
Partager la logique de rendu :
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url).then(r => r.json()).then(setData).finally(() => setLoading(false));
}, [url]);
return render({ data, loading });
}
// Utilisation
<DataFetcher
url="/api/utilisateurs"
render={({ data, loading }) =>
loading ? <Spinner /> : <ListeUtilisateurs utilisateurs={data} />
}
/>
Optimisation Next.js
Référence : references/nextjs_optimization_guide.md
Composants serveur vs client
Utilisez les Server Components par défaut. Ajoutez 'use client' uniquement lorsque vous avez besoin :
- Des gestionnaires d'événements (onClick, onChange)
- De l'état (useState, useReducer)
- Des effets (useEffect)
- Des API navigateur
// Server Component (par défaut) - pas de 'use client'
async function PageProduit({ params }) {
const product = await getProduct(params.id); // Récupération côté serveur
return (
<div>
<h1>{product.name}</h1>
<BoutonAjouterPanier productId={product.id} /> {/* Composant client */}
</div>
);
}
// Client Component
'use client';
function BoutonAjouterPanier({ productId }) {
const [adding, setAdding] = useState(false);
return <button onClick={() => addToCart(productId)}>Ajouter</button>;
}
Optimisation des images
import Image from 'next/image';
// Au-dessus de la ligne de flottaison - charger immédiatement
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority
/>
// Image responsive avec fill
<div className="relative aspect-video">
<Image
src="/produit.jpg"
alt="Produit"
fill
sizes="(max-width: 768px) 100vw, 50vw"
className="object-cover"
/>
</div>
Modèles de récupération de données
// Récupération parallèle
async function TableauDeBord() {
const [user, stats] = await Promise.all([
getUser(),
getStats()
]);
return <div>...</div>;
}
// Streaming avec Suspense
async function PageProduit({ params }) {
return (
<div>
<DetailsProduit id={params.id} />
<Suspense fallback={<SkeletonAvis />}>
<Avis productId={params.id} />
</Suspense>
</div>
);
}
Accessibilité et tests
Référence : references/frontend_best_practices.md
Liste de vérification d'accessibilité
- HTML sémantique : Utilisez des éléments appropriés (
<button>,<nav>,<main>) - Navigation au clavier : Tous les éléments interactifs doivent être focusables
- Étiquettes ARIA : Fournissez des étiquettes pour les icônes et les widgets complexes
- Contraste des couleurs : Minimum 4.5:1 pour le texte normal
- Indicateurs de focus : États de focus visibles
// Bouton accessible
<button
type="button"
aria-label="Fermer la boîte de dialogue"
onClick={onClose}
className="focus-visible:ring-2 focus-visible:ring-blue-500"
>
<XIcon aria-hidden="true" />
</button>
// Lien de saut pour les utilisateurs clavier
<a href="#contenu-principal" className="sr-only focus:not-sr-only">
Aller au contenu principal
</a>
Stratégie de test
// Test de composant avec React Testing Library
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('le bouton déclenche l\'action au clic', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Cliquez ici</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
// Test d'accessibilité
test('la boîte de dialogue est accessible', async () => {
render(<Dialog open={true} title="Confirmer" />);
expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(screen.getByRole('dialog')).toHaveAttribute('aria-labelledby');
});
Référence rapide
Configuration Next.js courante
// next.config.js
const nextConfig = {
images: {
remotePatterns: [{ protocol: 'https', hostname: 'cdn.example.com' }],
formats: ['image/avif', 'image/webp'],
},
experimental: {
optimizePackageImports: ['lucide-react', '@heroicons/react'],
},
};
Utilitaires Tailwind CSS
// Classes conditionnelles avec cn()
import { cn } from '@/lib/utils';
<button className={cn(
'px-4 py-2 rounded',
variant === 'primary' && 'bg-blue-500 text-white',
disabled && 'opacity-50 cursor-not-allowed'
)} />
Modèles TypeScript
// Props avec enfants
interface CardProps {
className?: string;
children: React.ReactNode;
}
// Composant générique
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}
Ressources
- Modèles React :
references/react_patterns.md - Optimisation Next.js :
references/nextjs_optimization_guide.md - Meilleures pratiques :
references/frontend_best_practices.md - Bibliothèque de questions de grillage (Matt Pocock grill) :
references/forcing_questions.md - Carte de composition (vers quel spécialiste se diriger) :
references/composition_map.md
Hypothèses et critères de succès vérifiables (discipline Karpathy)
Avant que cette compétence ne génère un composant, ne recommande un framework ou n'audite un bundle, les quatre hypothèses suivantes DOIVENT être présentées.
- Appareil utilisateur principal + réseau — mobile-4G, desktop-fibre, Android-bas-de-gamme, ou réseau-entreprise. Influence chaque décision de performance.
- Objectif LCP en millisecondes — un nombre unique, pas "rapide." Influence le budget du bundle et le choix du rendu.
- Dépendant du SEO vs. protégé par authentification — influence le rendu (SSR/SSG/RSC vs. SPA).
- Objectif WCAG + responsable a11y nommé — AA, AAA, ou meilleur effort. Influence l'investissement en a11y et les vérifications CI.
Critères de succès vérifiables (Karpathy #4) — chaque recommandation doit inclure :
- Les objectifs Core Web Vitals (LCP, INP, CLS) au p75 sur l'appareil principal
- Un budget de bundle JS par route en Ko-gzip
- Un plancher d'accessibilité Lighthouse + plancher de performance
Si l'un de ces trois éléments n'est pas spécifié, la recommandation est incomplète — revenez à la question Q2 de la bibliothèque de questions de grillage.
L'outil scripts/frontend_decision_engine.py intègre ces vérifications : il refuse de recommander un profil sans les quatre entrées d'hypothèses et affiche les seuils vérifiables pour le profil correspondant.
Profils de personnalisation
Quatre profils intégrés dans profiles/ calibrent chaque recommandation :
| Profil | Quand le choisir | Objectif LCP (mobile-4G p75) | Budget bundle |
|---|---|---|---|
next-app-router | SaaS destiné aux clients, SEO + dynamique, RSC d'abord | 2000ms | 150 Ko-gzip / route |
remix-or-sveltekit | Mobile-4G principal, peu de JS d'abord, amélioration progressive | 1500ms | 80 Ko-gzip / route |
vite-spa | Application protégée par authentification, bureau/entreprise principal | 2500ms | 200 Ko init + 80 Ko / route |
astro-or-static | Marketing / docs / blog, quasi-aucune écriture, SEO critique | 1200ms | 30 Ko JS / page |
Choisissez un profil via :
python scripts/frontend_decision_engine.py \
--primary-device mobile-4g --lcp-target-ms 2000 \
--seo-dependent true --auth-walled false --team-size 5
L'outil renvoie le profil le mieux adapté, le compromis du deuxième (à moins de 15 %), les choix de la pile, les anti-modèles à éviter pour ce profil et les vérifications CI requises.
Pour ajouter un profil personnalisé (par exemple, les paramètres par défaut de l'outil interne de votre organisation) : copiez profiles/vite-spa.json vers profiles/<votre-org>.json et ajustez constraints + success_thresholds.
Carte de composition
Cette compétence NE réimplémente PAS le champ couvert par les spécialistes de niveau PUISSANT. Elle les délègue. Voir references/composition_map.md pour la table de routage complète. Points clés :
| Problématique | Déléguer à |
|---|---|
| Audit WCAG, contraste, lecteur d'écran | engineering-team/skills/a11y-audit/ |
| Profilage du bundle + performances runtime | engineering/skills/performance-profiler/ |
| Page d'accueil cinématographique / storytelling défilant | engineering-team/skills/epic-design/ |
| Apple HIG (iOS / macOS / visionOS) | product-team/skills/apple-hig-expert/ |
| Revue pré-commit Karpathy | engineering/karpathy-coder/ |
| Grillage préliminaire d'architecture | engineering/grill-me/ |
L'agent cs-frontend-engineer orchestre ces délégations via context: fork. Invoquez-le depuis un autre agent avec Agent({subagent_type: "cs-frontend-engineer", prompt: "..."})