Introduction : Pourquoi changer l'URL sans rechargement est un super-pouvoir
\n\nImaginez : vous ouvrez un service web moderne — disons, un client de messagerie ou un éditeur de documents en ligne. Vous basculez entre les dossiers, ouvrez des e-mails, modifiez les paramètres — et la page ne se recharge pas. Pourtant, la barre d'adresse du navigateur change, les boutons « Précédent » et « Suivant » fonctionnent, et les favoris conservent l'état souhaité. Comment cela fonctionne-t-il ? La magie de la modification d'URL sans rechargement de page.
\n\nCe n'est pas qu'une simple « astuce » — c'est le fondement des applications monopages (SPA) performantes. Autrefois, les développeurs utilisaient les hashs (#), mais c'était inesthétique et peu pratique. Aujourd'hui, nous avons l'History API — un outil puissant qui permet de modifier l'URL instantanément, sans scintillement, sans perte d'état, sans requêtes HTTP superflues.
\n\nDans cet article, nous allons voir comment fonctionnent pushState et replaceState, comment ils impactent les performances, et comment éviter les pièges lors de l'optimisation. C'est parti !
Les bases : l'History API et ses méthodes
\n\nLa spécification HTML5 nous a offert l'interface window.history. Les deux principales méthodes pour modifier l'URL sans rechargement sont pushState et replaceState.
pushState — ajouter une nouvelle entrée dans l'historique
\n\npushState crée une nouvelle entrée dans l'historique du navigateur. L'utilisateur peut appuyer sur « Précédent » et revenir à l'URL précédente.
// Syntaxe\nhistory.pushState(state, title, url);\n\n// Exemple\nhistory.pushState({ page: 1 }, 'Accueil', '/accueil');\n\n\nParamètres :
\n- \n
- state — un objet de données associé à cette entrée. Vous pouvez stocker toutes les données sérialisables (par exemple, l'état d'un filtre, l'ID d'un élément). \n
- title — le titre de la page (la plupart des navigateurs l'ignorent, mais il est préférable de passer une chaîne vide ou un texte pertinent). \n
- url — la nouvelle URL. Elle doit être relative ou absolue, mais dans la même origine (protocole + domaine + port). \n
replaceState — remplacer l'entrée courante
\n\nreplaceState modifie l'entrée courante dans l'historique sans en créer une nouvelle. L'utilisateur ne pourra pas revenir « en arrière » vers l'URL précédente — elle sera remplacée.
// Syntaxe\nhistory.replaceState(state, title, url);\n\n// Exemple — mise à jour de l'URL lors du changement de filtre\nfunction applyFilter(filterName) {\n history.replaceState({ filter: filterName }, '', `?filter=${filterName}`);\n updateUI(filterName);\n}\n\n\npopstate — écouter les changements d'historique
\n\nLorsque l'utilisateur appuie sur « Précédent » ou « Suivant », le navigateur génère l'événement popstate. Important : pushState et replaceState ne déclenchent pas cet événement — il doit être traité séparément.
window.addEventListener('popstate', function(event) {\n if (event.state) {\n // Restaurer l'état à partir des données de event.state\n restorePageState(event.state);\n } else {\n // Si state est vide — l'utilisateur est peut-être sorti du cadre de notre application\n loadDefaultState();\n }\n});\n\n\nOptimisation des performances : ce qu'il faut savoir
\n\nLa modification d'URL est rapide. Mais il y a des subtilités qui peuvent transformer un super-pouvoir en source de bugs.
\n\n1. N'appelez pas pushState trop souvent
\n\nChaque appel à pushState ajoute une entrée dans l'historique. Si vous l'appelez à chaque mouvement de souris ou à chaque caractère saisi dans une recherche — l'historique gonflera, et l'utilisateur devra appuyer 100 fois sur « Précédent » pour quitter le filtre. Utilisez replaceState pour les états intermédiaires, et pushState pour les actions logiquement complètes (par exemple, l'ouverture d'une nouvelle page).