Man searching on Zumper with laptop

Chez Zumper, nous veillons à maintenir notre infrastructure technologique à jour afin d'offrir une expérience utilisateur optimale. Le site web de Zumper a connu de nombreuses évolutions au fil des ans et, tout récemment, nous avons finalisé sa migration d'Angular vers React.

En général, le web change radicalement tous les deux ans environ, les navigateurs s'améliorant constamment. Tout développeur web ayant quelques années d'expérience se souvient avec nostalgie des périodes où il fallait tout reconstruire à chaque nouvelle version majeure d'un framework. Cette nouvelle innovation est souvent synonyme d'une meilleure expérience utilisateur.

C’est ainsi que, chez Zumper, nous nous sommes lancés dans le long voyage de la mise à niveau de notre interface web principale, passant d’Angular à React.

Nous sommes ravis de proposer les aspects clés de l'expérience client sur une base de code moderne. Mieux encore, nous avons pu la lancer sans aucune interruption pour nos clients. Il a fallu 18 mois et un effort collectif colossal avant le déploiement complet de React. Bien que certaines parties de notre application soient encore gérées par Angular, nous les migrerons vers React en 2019. À l'avenir, toutes les nouvelles fonctionnalités seront développées avec React.

Note: Dans cet article, nous utilisons les termes “ rendu côté serveur ” et “ rendu côté client ”. Le rendu côté serveur correspond aux opérations effectuées sur les serveurs de Zumper avant l'envoi des données au navigateur, tandis que le rendu côté client correspond aux opérations effectuées dans le navigateur.

Rendu côté serveur

Dès le départ, nous savions qu'il nous fallait prendre en charge le rendu côté serveur. Ce dernier représente un gain de performance crucial, permettant de gagner de précieuses millisecondes lors du chargement initial de la page. Il améliore également la capacité des moteurs de recherche à indexer notre contenu. En définitive, cela signifie que nous mettons tout en œuvre pour aider les locataires à trouver l'appartement idéal.

Une application React classique est rendue uniquement côté client. Avec ce type de rendu, tout le contenu est chargé par JavaScript dans le navigateur. Cela convient aux projets de démarrage et aux applications simples, mais ce n'était pas adapté à Zumper. En effet, une bonne partie de nos clients nous trouvent via un moteur de recherche. Or, les moteurs de recherche (et les clients) privilégient les sites web dont le contenu initial se charge rapidement. Nous ne pouvions donc pas nous contenter d'afficher une page blanche ; le rendu côté serveur était indispensable.

React a une bonne histoire pour composants de rendu sur le serveur et les hydrater dans le navigateur. Dès le début de notre projet, nous disposions d'un serveur Express capable d'afficher notre site côté serveur – et cela fonctionnait parfaitement ! Les deux principaux obstacles rencontrés étaient le préchargement des données et la prise en charge du découpage du code.

Préchargement des données dans Redux

Nous utilisons Redux comme système de stockage de données côté client. Dans une application côté client, vous pouvez utiliser react-router pour déterminer les composants à afficher, puis déclencher une action pour charger les données de la route correspondante. Côté serveur, la procédure est identique, mais elle doit être effectuée avant l'affichage de l'arborescence des composants.

Au départ, la difficulté avec Redux côté serveur résidait dans l'impossibilité d'envoyer des requêtes pour récupérer des données de manière asynchrone. Le rendu React côté serveur doit être synchrone. Nous avons donc finalement opté pour la solution suivante : chargement de données L'exemple du guide react-router est fourni. Cependant, le code est incomplet. Nous avons dû combiner l'exemple de chargement de données avec… configuration du routeur React un exemple pour parvenir à une solution complète de bout en bout.

Le plus délicat consistait à éviter que les données préchargées sur le serveur ne soient immédiatement récupérées côté client. Il nous fallait gérer le routage côté serveur et côté client, et suivre les phases d'hydratation de l'application par rapport aux changements de page côté client (nous y reviendrons plus loin).

Notre code final sera à terme publié en open source. Il nous permet de faire ce qui suit :

  • Déterminer quel arbre de composants correspond à notre route actuelle sur le serveur
  • Précharger les données dans Redux (nous autorisons les routes imbriquées à gérer indépendamment leurs besoins de récupération de données)
  • Ignorer le chargement des données lors du rendu initial côté client
  • Déclencher le chargement des données côté client lors d'un changement d'itinéraire

Prise en charge du fractionnement du code

Pour le fractionnement du code, nous avons finalement opté pour chargeable React. Cette bibliothèque nous a permis de découper le code de l'application en “ bundles ” plus petits grâce à Webpack. Côté client, cela signifie que nos utilisateurs ne téléchargent que le code JavaScript nécessaire à l'affichage de la page. Côté serveur, cela peut engendrer des problèmes.

Côté serveur, nous ne souhaitions pas utiliser le chargement différé pour nos composants ; le rendu côté serveur de React exige un chargement synchrone (voir ci-dessus). De plus, côté client, nous devions impérativement disposer de tout le code JavaScript nécessaire pour éviter les problèmes d'hydratation.

La documentation de react-loadable contient des exemples de configuration de Webpack et Babel pour activer le découpage du code côté serveur et côté client. Notre principal obstacle a été d'attendre la maturation de ce projet. La première version que nous avons utilisée ne répondait pas entièrement à nos besoins. Une fois passés à la version 5.x, tout fonctionnait beaucoup mieux !

En résumé, cela fonctionne comme ceci :

  1. Précharger tous les ensembles de code fractionnés sur le serveur
  2. Précharger toutes les données de la route dans Redux
  3. Générer les composants sur le serveur ; déterminer les modules à inclure dans le HTML ; servir le HTML et les données préchargées au client
  4. Précharger les modules sur le client
  5. Remplir le composant Redux côté client avec les données préchargées
  6. Hydrater les composants

Cela semble compliqué, mais notre stratégie de chargement de données et react-loadable ont rendu la tâche extrêmement simple.

Il convient de noter que React va bientôt sortir une nouvelle version. React.lazy composant qui résout le problème de la division du code côté client ; mais, bien sûr, il ne fonctionne pas pour le rendu côté serveur. Ils recommandent désormais composants chargeables pour le rendu côté serveur, il est donc très probable que Zumper modifie notre façon de procéder au découpage du code en 2019.

Routage côté client

Zumper on iPhone

Très tôt dans nos expérimentations, nous avons réussi à faire fonctionner le rendu côté serveur. En revanche, il nous a fallu beaucoup plus de temps pour comprendre comment activer le routage côté client. React Router nous permet de modifier l'URL sans recharger la page (grâce à la méthode `require()`). histoire Cela signifie que lorsque les clients interagissent avec notre page, tout est géré dans le navigateur, sans aller-retour au serveur. Le routage côté client offre une expérience utilisateur nettement supérieure, notamment sur mobile où la vitesse de chargement des pages est primordiale.

Nos premières tentatives n'ont pas permis d'exploiter pleinement le potentiel de nos méthodes. React Router. Notre code était un peu complexe et difficile à gérer, nous sommes donc revenus à l'essentiel. La clé de notre approche finale de routage côté client a été de considérer l'URL comme une source de données à part entière.

Au départ, nous utilisions Redux comme source unique de vérité. Cela était parfaitement logique lors du chargement initial de la page, car toutes les données étaient préchargées par le serveur. Cependant, ce n'était plus forcément le cas une fois le client entré en jeu. Nous avons dû examiner attentivement notre application et nous assurer que, lors d'un changement d'URL, nous utilisions bien cette dernière comme source de vérité. Tout comme côté serveur, le store Redux côté client doit être entièrement dérivé de l'URL.

La première étape a consisté à repenser notre stratégie de chargement des données afin de couvrir les cas d'utilisation côté serveur et côté client (voir ci-dessus). Pour rappel, cela impliquait d'utiliser les données préchargées lors du chargement initial de la page, puis de récupérer les données côté client après chaque modification d'URL. Nous avons finalement déclenché une action lors du premier montage du composant de l'application afin de déterminer si l'hydratation était en cours.

La deuxième étape a consisté à retravailler notre code pour privilégier l'URL. Cela s'est avéré être un travail colossal, car nous avions initialement emprunté une autre voie. Certains composants de haut niveau effectuaient un travail considérable pour concilier l'URL et l'état Redux (ce qui impliquait une utilisation abusive de…). componentWillReceivePropsEn adoptant une stratégie privilégiant les URL, nous avons pu supprimer des centaines de lignes de code de ces composants et Cessez de vous fier à des fonctionnalités obsolètes. Des tests approfondis ont été nécessaires pour s'assurer que nous ne cassions aucune fonctionnalité existante, mais cela a finalement abouti à un code plus simple et plus facile à maintenir.

React-router personnalisé

Nous devions prendre en charge certains scénarios de routage avancés qui reposaient sur des expressions régulières plus complexes que celles prises en charge nativement par react-router. Nous avons donc fini par publier notre propre solution. @zumper/react-router Ce package disponible sur NPM permet cette fonctionnalité. En interne, il expose une méthode permettant de compiler des expressions régulières personnalisées pour une route. Les packages sous-jacents utilisés par react-router pour la correspondance des routes prenaient déjà en charge cette fonctionnalité ; nous l'avons simplement rendue accessible pour une utilisation dans une route classique.

Nous n'avons pas encore terminé.

Nous ne comptons pas nous arrêter là. Nous continuons à optimiser les performances et nous en déploierons certaines (espérons-le) avant la fin de l'année. 2019 sera une année passionnante pour Zumper, car nous migrons le reste de l'expérience Angular vers notre base de code React. Plus enthousiasmant encore, nous allons enrichir l'offre Zumper pour nos clients et continuer à révolutionner la recherche et la location d'appartements. Et désormais, nous pouvons le faire sur une base solide et à la pointe de la technologie.

Envie de rejoindre l'équipe Zumper ? Nous recherchons constamment des ingénieurs, des designers et des responsables commerciaux brillants, passionnés et motivés pour nous aider à créer des produits exceptionnels et à révolutionner ce secteur. Consultez nos offres d'emploi. opportunités de carrière actuelles ou envoyez-nous un courriel à jobs@zumper.com.

Trouvez votre prochain endroit