Fermer

novembre 2, 2020

Comment organiser une grande application React et la faire évoluer


Cet article a été rédigé par l'auteur invité Jack Franklin . Les messages d'invités de SitePoint visent à vous proposer du contenu attrayant rédigé par d'éminents écrivains et orateurs de la communauté Web.

Dans cet article, je vais discuter de l'approche que j'adopte lors de la création et de la structuration de grandes applications React. L'une des meilleures fonctionnalités de React est de savoir comment il se débrouille et est tout sauf descriptif en ce qui concerne la structure des fichiers. Par conséquent, vous trouverez de nombreuses questions sur Stack Overflow et des sites similaires vous demandant comment structurer les applications. C’est un sujet très opiniâtre et il n’existe pas de solution unique. Dans cet article, je vais vous parler des décisions que je prends lors de la création d'applications React: choisir des outils, structurer des fichiers et fractionner des composants en plus petits morceaux.

 Un astronaute construisant une colonie spatiale sous la forme du logo React [19659004] Ce ne sera pas une surprise pour certains d'entre vous que je sois un grand fan de webpack pour construire mes projets. Bien qu'il s'agisse d'un outil compliqué, l'excellent travail réalisé dans la version 5 par l'équipe et le nouveau site de documentation <a href= le rendent beaucoup plus facile. Une fois que vous entrez dans Webpack et que vous avez les concepts en tête, vous avez vraiment une puissance incroyable à exploiter. J'utilise Babel pour compiler mon code, y compris les transformations spécifiques à React comme JSX, et le webpack-dev-server pour servir mon site localement. Personnellement, je n'ai pas trouvé que le rechargement à chaud me procurait autant d'avantages, donc je suis plus que satisfait de webpack-dev-server et de son actualisation automatique de la page.

J'utilise des modules ES, introduits pour la première fois dans ES2015 (qui est transpilé via Babel) pour importer et exporter des dépendances. Cette syntaxe existe depuis un certain temps maintenant, et bien que webpack puisse prendre en charge CommonJS (aka, importations de style Node), il est logique pour moi de commencer à utiliser le dernier et le meilleur. De plus, webpack peut supprimer le code mort des bundles à l'aide des modules ES2015 ce qui, bien que pas parfait, est une fonctionnalité très pratique à avoir, et qui deviendra plus bénéfique à mesure que la communauté se dirigera vers la publication de code vers npm dans ES2015 . La majorité de l'écosystème Web a évolué vers les modules ES, c'est donc un choix évident pour chaque nouveau projet que je lance. C'est également ce que la plupart des outils s'attendent à prendre en charge, y compris d'autres bundles comme Rollup si vous préférez ne pas utiliser Webpack.

Structure des dossiers

Il n'existe pas de structure de dossier correcte pour toutes les applications React. (Comme pour le reste de cet article, vous devriez le modifier selon vos préférences.) Mais voici ce qui a bien fonctionné pour moi.

Le code vit dans src

Pour garder les choses organisées, je vais placer tout le code d'application dans un dossier appelé src . Celui-ci ne contient que du code qui se termine dans votre bundle final, et rien de plus. Ceci est utile car vous pouvez dire à Babel (ou à tout autre outil qui agit sur le code de votre application) de simplement regarder dans un répertoire et de vous assurer qu'il ne traite aucun code dont il n'a pas besoin. D'autres codes, tels que les fichiers de configuration Webpack, se trouvent dans un dossier correctement nommé. Par exemple, ma structure de dossiers de niveau supérieur contient souvent:

 - src => code d'application ici
- webpack => configurations webpack
- scripts => tous les scripts de construction
- tests => tout code spécifique au test (API mocks, etc.)

En général, les seuls fichiers qui seront au niveau supérieur sont index.html package.json et tous les fichiers dot, tels que .babelrc . Certains préfèrent inclure la configuration de Babel dans package.json mais je trouve que ces fichiers peuvent devenir volumineux sur des projets plus volumineux avec de nombreuses dépendances, donc j'aime utiliser .eslintrc . babelrc et ainsi de suite.

Composants React

Une fois que vous avez un dossier src le plus délicat est de décider comment structurer vos composants. Auparavant, je mettais tous les composants dans un seul grand dossier, tel que src / components mais j'ai constaté que sur des projets plus importants, cela devient très vite écrasant.

Une tendance courante est de ont des dossiers pour les composants «intelligents» et «stupides» ( également appelés composants «conteneur» et «présentation» ), mais personnellement, je n'ai jamais trouvé que les dossiers explicites fonctionnent pour moi. Bien que j'aie des composants qui se classent vaguement en «intelligents» et «stupides» (j'en parlerai plus en détail ci-dessous), je n'ai pas de dossiers spécifiques pour chacun d'eux.

Nous avons regroupé les composants en fonction du domaines de l'application où ils sont utilisés, ainsi qu'un dossier core pour les composants courants utilisés partout (boutons, en-têtes, pieds de page – composants génériques et très réutilisables). Le reste des dossiers correspond à une zone spécifique de l'application. Par exemple, nous avons un dossier appelé panier qui contient tous les composants relatifs à l'affichage du panier, et un dossier appelé listings qui contient le code pour lister les choses que les utilisateurs peuvent acheter sur une page.

La catégorisation dans des dossiers signifie également que vous pouvez éviter de préfixer les composants avec la zone de l'application pour laquelle ils sont utilisés. Par exemple, si nous avions un composant qui restitue le coût total du panier de l'utilisateur, plutôt que de l'appeler CartTotal je préférerais peut-être utiliser Total car je l'importe depuis le cart dossier:

 import   Total   from   '../ cart / total' 

 import   CartTotal   from   '../ panier / panier-total '

C'est une règle que je me retrouve parfois à enfreindre. Le préfixe supplémentaire peut clarifier, en particulier si vous avez deux à trois composants nommés de manière similaire, mais souvent cette technique peut éviter une répétition supplémentaire des noms.

Préférez l'extension jsx aux lettres majuscules

Beaucoup de les gens nomment les composants React avec une majuscule dans le fichier, pour les distinguer des fichiers JavaScript normaux. Ainsi, dans les importations ci-dessus, les fichiers seraient CartTotal.js ou Total.js . J'ai tendance à préférer m'en tenir aux fichiers en minuscules avec des tirets comme séparateurs, donc pour distinguer j'utilise l'extension .jsx pour les composants React. Par conséquent, je m'en tiendrai à cart-total.jsx .

Cela a le petit avantage supplémentaire de pouvoir rechercher facilement dans vos fichiers React en limitant votre recherche aux fichiers avec . jsx et vous pouvez même appliquer des plugins webpack spécifiques à ces fichiers si vous en avez besoin.

Quelle que soit la convention de dénomination que vous choisissez, l'important est que vous vous y teniez. Avoir une combinaison de conventions dans votre base de code deviendra rapidement un cauchemar à mesure qu'il grandit et que vous devez y naviguer. Vous pouvez appliquer cette convention .jsx en utilisant une règle d'eslint-plugin-react .

Un composant React par fichier

Suite à la règle précédente, nous nous en tenons à une convention d'un fichier de composant React, et le composant doit toujours être l'exportation par défaut.

Normalement, nos fichiers React ressemblent à ceci:

 import   React   from   'react' 

 export   default   function   Total  ( props )   {} 

Dans le cas où nous devons envelopper le composant afin de le connecter à un magasin de données Redux, par exemple, le composant entièrement enveloppé devient l'exportation par défaut:

 import   React   ] {  Component   PropTypes  }   from   'react' 
 import   { connect }   from   ] 'react-redux' 

 export   default   function   Total  ( props )   {} 

 export   default   connect  ( ()   =>   {} ) [19659043] ( Total ) 

Vous remarquerez que nous exportons toujours le composant d'origine. C'est vraiment utile pour les tests, où vous pouvez travailler avec le composant «simple» sans avoir à configurer Redux dans vos tests unitaires.

En conservant le composant comme exportation par défaut, il est facile d'importer le composant et de savoir comment pour y arriver, plutôt que d'avoir à chercher le nom exact. Un inconvénient de cette approche est que la personne qui importe peut appeler le composant comme bon lui semble. Une fois de plus, nous avons une convention pour cela: l'importation doit être nommée d'après le fichier. Par conséquent, si vous importez total.jsx le composant doit être importé sous la forme Total . user-header.jsx devient UserHeader et ainsi de suite.

Il convient de noter que la règle d'un composant par fichier n'est pas toujours suivie. Si vous finissez par créer un petit composant pour vous aider à rendre une partie de vos données et qu’il ne sera utilisé qu’à un seul endroit, il est souvent plus facile de le laisser dans le même fichier que le composant qui l’utilise. Il y a un coût à conserver des composants dans des fichiers séparés: il y a plus de fichiers, plus d'importations et généralement plus à suivre en tant que développeur, alors voyez si cela en vaut la peine. Comme la plupart des suggestions de cet article, ce sont des règles avec des exceptions.

Composants React «intelligents» et «stupides»

J'ai brièvement mentionné la séparation des composants «intelligents» et «stupides», et c'est quelque chose que nous adhérons dans notre base de code. Bien que nous ne le reconnaissions pas en les divisant en dossiers, vous pouvez diviser globalement notre application en deux types de composants:

  • composants «intelligents» qui manipulent les données, se connectent à Redux et traitent les interactions de l'utilisateur
  • «dumb ”Composants qui reçoivent un ensemble d'accessoires et rendent certaines données à l'écran

Vous pouvez en savoir plus sur la façon dont nous visons les composants“ stupides ”dans mon article de blog sur les composants fonctionnels sans état dans React . Ces composants constituent la majorité de notre application, et vous devriez toujours préférer ces composants si possible. Ils sont plus faciles à utiliser, moins bogués et plus faciles à tester.

Même lorsque nous devons créer des composants «intelligents», nous essayons de conserver toute la logique JavaScript dans son propre fichier. Idéalement, les composants qui doivent manipuler des données devraient les transmettre à un JavaScript capable de les manipuler. En faisant cela, le code de manipulation peut être testé séparément de React, et vous pouvez vous en moquer si nécessaire lors du test de votre composant React.

Évitez les grands render Methods

Alors que ce point faisait référence à la méthode render définie sur les composants de la classe React, ce point est toujours valable lorsque vous parlez de composants fonctionnels, en ce sens que vous devez faire attention à un composant qui rend un morceau de HTML inhabituellement volumineux.

Une chose que nous recherchons est d'avoir de nombreux petits composants React, plutôt que moins de composants plus grands. La taille de la fonction de rendu est un bon guide lorsque votre composant devient trop grand. Si elle devient lourde ou si vous devez la diviser en plusieurs fonctions de rendu plus petites, le moment est peut-être venu d’envisager de soustraire une fonction.

Ce n’est pas une règle absolue; vous et votre équipe devez avoir une idée de la taille du composant qui vous convient avant de retirer d’autres composants, mais la taille de la fonction de rendu du composant est un bon instrument de mesure. Vous pouvez également utiliser le nombre d'accessoires ou d'objets en état comme un autre bon indicateur. Si un composant utilise sept accessoires différents, cela peut indiquer qu'il en fait trop.

Always Use prop-type

React vous permet de documenter les noms et les types de propriétés que vous s'attendre à ce qu'un composant soit donné à l'aide de son package prop-types .

En déclarant les noms et les types d'accessoires attendus, ainsi que s'ils sont facultatifs ou non, vous pouvez être plus sûr que vous ' vous avez les bonnes propriétés lorsque vous travaillez avec des composants, et vous pouvez passer moins de temps à déboguer si vous avez oublié un nom de propriété ou lui avez donné le mauvais type. Vous pouvez appliquer cela en utilisant la règle PropTypes eslint-plugin-react .

Bien que prendre le temps de les ajouter peut sembler infructueux, lorsque vous le faites, vous vous remercierez lorsque vous viendrez à réutiliser un composant vous avez écrit il y a six mois.

Redux

Nous utilisons également Redux dans beaucoup de nos applications pour gérer les données de notre application, et comment structurer les applications Redux est une autre question très courante, avec de nombreuses opinions divergentes.

Le gagnant pour nous est Ducks une proposition qui place les actions, le réducteur et les créateurs d'action pour chaque partie de votre candidature dans un seul fichier. Encore une fois, bien que ce soit celui qui a fonctionné pour nous, choisir et s'en tenir à une convention est la chose la plus importante ici.

Plutôt que d'avoir reducers.js et actions.js où chacun contient des bits de code liés les uns aux autres, le système Ducks fait valoir qu'il est plus logique de regrouper le code associé dans un seul fichier. Supposons que vous ayez une boutique Redux avec deux clés de niveau supérieur, user et posts . La structure de votre dossier ressemblerait à ceci:

 canards
- index.js
- user.js
- posts.js

index.js contiendrait le code qui crée le réducteur principal – probablement en utilisant combineReducers de Redux pour le faire – et dans user.js et posts.js vous placez tout le code pour ceux qui ressembleront normalement à ceci:



 const   LOG_IN   =   'LOG_IN' 

 export   const [19659109] logIn   =   nom   =>   ( { type :   LOG_IN  nom }  ) 

 export   default   function   reducer  ( state  =   {}  action ) [19659044] {} 

Cela vous évite d'avoir à importer des actions et des créateurs d'actions à partir de fichiers différents et garde le code des différentes parties de votre boutique côte à côte.

Modules JavaScript autonomes

Bien que l'objet de cet article soit été sur les composants React, lors de la création d'une application React, vous vous surprendrez à écrire beaucoup de code entièrement séparé de React. C'est l'une des choses que j'aime le plus dans le framework: une grande partie du code est entièrement découplée de vos composants.

Chaque fois que vous trouvez que votre composant se remplit de logique métier qui pourrait être déplacé hors du composant, je recommande Ce faisant. D'après mon expérience, nous avons constaté qu'un dossier appelé lib ou services fonctionne bien ici. Le nom spécifique n'a pas d'importance, mais un dossier rempli de "composants non-React" est vraiment ce que vous recherchez.

Ces services exportent parfois un groupe de fonctions, ou parfois un objet de fonctions associées. Par exemple, nous avons services / local-storage.js qui offre un petit wrapper autour de l'API native window.localStorage :



 const   LocalStorage   =   {
   get  ()   {} 
   set  ()   {} [19659043]} 

 exportation   par défaut   LocalStorage 

Garder votre logique à l'écart de composants comme celui-ci présente de très grands avantages:

  1. vous pouvez tester ce code de manière isolée sans avoir besoin de rendre des composants React
  2. dans vos composants React, vous pouvez stub les services pour qu'ils se comportent et renvoyez les données que vous voulez pour le test spécifique

Tests

Comme mentionné ci-dessus, nous testons notre code de manière très approfondie et nous en sommes venus à nous fier au Jest framework de Facebook comme meilleur outil pour le travail. Il est très rapide, il gère de nombreux tests, il s’exécute rapidement en mode montre et vous donne un retour rapide, et il est livré avec des fonctions pratiques pour tester React dès la sortie de la boîte. J'ai beaucoup écrit à ce sujet sur SitePoint auparavant donc je n'entrerai pas dans beaucoup de détails à ce sujet ici, mais je vais parler de la façon dont nous structurons nos tests.

Dans le passé, j'étais s'est engagé à avoir un dossier tests séparé contenant tous les tests pour tout. Donc, si vous aviez src / app / foo.jsx vous auriez également tests / app / foo.test.jsx . En pratique, à mesure qu'une application s'agrandit, il est plus difficile de trouver les bons fichiers, et si vous déplacez des fichiers dans src vous avez souvent oublié de les déplacer dans test et le les structures se désynchronisent. De plus, si vous avez un fichier dans tests qui doit importer le fichier dans src vous vous retrouvez avec des importations très longues. Je suis sûr que nous avons tous rencontré ceci:

 import   Foo   from   '../../../ src / app / foo' 

Ils sont difficiles à utiliser et à corriger si vous changez la structure des répertoires.

En revanche, placer chaque fichier de test à côté de son fichier source évite tous ces problèmes. Pour les distinguer, nous suffixons nos tests avec .spec – bien que d'autres utilisent .test ou simplement -test – mais ils coexistent avec le code source, avec le même nom sinon:

 - panier
  - total.jsx
  - total.spec.jsx
- prestations de service
  - local-storage.js
  - local-storage.spec.js

Au fur et à mesure que les structures des dossiers changent, il est facile de déplacer les bons fichiers de test, et c'est aussi incroyablement évident lorsqu'un fichier n'a pas de tests, donc vous pouvez repérer ces problèmes et les résoudre.

Conclusion

Là Il existe de nombreuses façons d'écorcher un chat, et il en va de même pour React. L'une des meilleures fonctionnalités du cadre est de savoir comment il vous permet de prendre la plupart des décisions concernant l'outillage, de créer des outils et des structures de dossiers, et vous devez l'accepter. J'espère que cet article vous a donné quelques idées sur la façon dont vous pourriez aborder vos applications React plus larges, mais vous devriez prendre mes idées et les modifier en fonction de vos préférences et de celles de votre équipe.






Source link