Pourquoi Optimiser les performances Next.js ?
Dans un environnement où l'expérience utilisateur est la clé, l'optimisation des performances de votre application est essentielle. En tant que développeur senior Next.js, vous savez bien que même une petite amélioration peut faire une différence significative. Un cas concret serait le développement d'une plateforme e-commerce : même un délai de quelques centièmes de seconde peut entraîner une baisse considérable des conversions.
Prerequis
Connaissances nécessaires :
- Familiarité avec Next.js
- Compréhension de la gestion des composants React
- Connaissance des concepts de routing et de state management (useState, useContext)
- Expérience avec les hooks personnalisés
Outils à installer :
- Node.js (v14.0.0 ou plus tard)
- npm (v6.0.0 ou plus tard)
- Yarn (v1.22.0 ou plus tard) [Optionnel]
- Editor de code (VSCode recommandé)
Concepts fondamentaux
1. Pre-rendering
Le pre-rendering est une technique où Next.js génère des pages HTML statiques à l'avance lors du build, plutôt que de les rendre au serveur ou côté client.
Schéma mental :
[Build] -> [HTML Statique]
Code fonctionnel :
// pages/index.js
import { useEffect } from 'react';
const Home = () => {
useEffect(() => {
console.log('Page rendered at build time');
}, []);
return <div>Welcome to the Next.js App!</div>;
};
export default Home;
2. Server-Side Rendering (SSR)
Le SSR génère le HTML côté serveur pour chaque requête entrante.
Schéma mental :
[Request] -> [Render on Server] -> [HTML Sent to Client]
Code fonctionnel :
// pages/about.js
import { useState, useEffect } from 'react';
const About = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
<div>
{data ? <p>{data.message}</p> : <p>Loading...</p>}
</div>
);
};
export default About;
3. Static Generation with Data Fetching
Combinant le pre-rendering statique et les données dynamiques, Next.js vous permet de générer des pages statiques au build avec des données dynamiques.
Schéma mental :
[Build] -> [HTML with Dynamic Data]
Code fonctionnel :
// pages/posts/[id].js
import { useEffect, useState } from 'react';
const Post = ({ post }) => {
const [data, setData] = useState(post);
useEffect(() => {
fetch(`https://api.example.com/posts/${post.id}`)
.then(response => response.json())
.then(data => setData(data));
}, [post.id]);
return (
<div>
{data ? <p>{data.title}</p> : <p>Loading...</p>}
</div>
);
};
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
paths: posts.map(post => `/posts/${post.id}`),
fallback: false,
};
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.id}`);
const post = await res.json();
return { props: { post } };
}
Mise en pratique : projet fil rouge
Mini-projet réaliste : Gestionnaire de tâches
Étape 1 : Configuration du projet
npx create-next-app@latest task-manager
cd task-manager
npm install react-hook-form @tanstack/react-query
Étape 2 : Création des composants
components/TaskList.js
import { useEffect, useState } from 'react';
import axios from 'axios';
const TaskList = () => {
const [tasks, setTasks] = useState([]);
useEffect(() => {
fetch('http://localhost:3001/tasks')
.then(response => response.json())
.then(data => setTasks(data));
}, []);
return (
<div>
{tasks.map(task => (
<div key={task.id}>
<h3>{task.title}</h3>
<p>{task.description}</p>
</div>
))}
</div>
);
};
export default TaskList;
components/TaskForm.js
import { useForm } from 'react-hook-form';
const TaskForm = () => {
const { register, handleSubmit, reset } = useForm();
const onSubmit = data => {
axios.post('http://localhost:3001/tasks', data)
.then(() => {
alert('Task added successfully');
reset();
});
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('title')} placeholder="Title" required />
<textarea {...register('description')} placeholder="Description" required />
<button type="submit">Add Task</button>
</form>
);
};
export default TaskForm;
Étape 3 : Structure des fichiers
pages/index.js
import TaskList from '../components/TaskList';
import TaskForm from '../components/TaskForm';
const Home = () => {
return (
<div>
<h1>Task Manager</h1>
<TaskForm />
<TaskList />
</div>
);
};
export default Home;
Étape 4 : Démarrez le serveur
npm run dev
Erreurs fréquentes et debugging
1. Error: Cannot find module 'react-hook-form'
Code incorrect :
import useHookForm from 'react-hook-form'; // Error
Code correct :
import { useForm } from 'react-hook-form'; // Correct
2. TypeError: Cannot read property 'data' of undefined
Code incorrect :
const { data } = useQuery('tasks', fetchTasks); // Error if fetchTasks is not defined
Code correct :
import { useQuery } from '@tanstack/react-query';
const fetchTasks = async () => {
const response = await axios.get('http://localhost:3001/tasks');
return response.data;
};
const { data, isLoading } = useQuery('tasks', fetchTasks); // Correct
3. TypeError: Cannot read property 'title' of undefined
Code incorrect :
<div>{task.title}</div> // Error if task is not defined
Code correct :
<div>{task ? task.title : 'Loading...'}</div> // Correct
Pour aller plus loin
- Optimisation des images avec Next.js : Documentation
- Utilisation de
react-querypour la gestion de données asynchrones : Documentation - Création d'un CLI tool avec Next.js : Documentation
Défi pratique
Construire une application simple de gestion de contacts en utilisant les concepts appris, notamment le pre-rendering statique et le server-side rendering.