Aperçu du code source

Cette section vous donne un aperçu de l’organisation du code source de React, de ses conventions et de son implémentation.

Si vous souhaitez contribuer à React, nous espérons que ce guide vous aidera à vous sentir plus à l’aise pour apporter des modifications.

Nous ne recommandons pas nécessairement ces conventions dans les applications React. Nombre d’entre elles existent pour des raisons historiques et sont susceptibles d’évoluer avec le temps.

Dépendances externes

React n’a presque pas de dépendances externes. Habituellement, un require() pointe vers un fichier dans le code de React lui-même. Cependant, il existe quelques exceptions relativement rares.

Le dépôt fbjs existe parce que React partage quelques petits utilitaires avec des bibliothèques telles que Relay et que nous les gardons synchronisées. Nous ne dépendons pas de petits modules équivalents dans l’écosystème Node, car nous souhaitons que les ingénieurs de Facebook puissent les modifier à tout moment. Aucun des utilitaires contenus dans fbjs n’est considéré comme une API publique et ils ne doivent être utilisés que par des projets Facebook tels que React.

Dossiers racines

Après avoir cloné le dépôt React, vous verrez quelques dossiers racines :

  • packages contient des métadonnées (telles que package.json) et le code source (sous-répertoire src) de tous les paquets du dépôt React. Si votre modification est liée au code, vous passerez le plus clair de votre temps dans le sous-répertoire src des différents paquets.
  • fixtures contient quelques petites applications React de test pour les contributeurs.
  • build est la sortie de construction de React. Il ne figure pas dans le dépôt, mais il apparaîtra dans votre clone de React après que vous l’aurez construit pour la première fois.

La documentation est hébergée dans un dépôt distinct de React.

Il existe quelques autres dossiers racines, mais ils sont principalement utilisés par l’outillage et vous n’aurez probablement jamais affaire à eux lorsque vous contribuerez.

Tests colocalisés

Nous n’avons pas de répertoire racine pour les tests unitaires. Nous les plaçons plutôt dans un répertoire appelé __tests__ situé à côté des fichiers qu’ils testent.

Par exemple, un test pour setInnerHTML.js sera placé juste à côté, dans __tests__/setInnerHTML-test.js.

Avertissements et invariants

Le code source de React utilise le module warning pour afficher les avertissements :

var warning = require('warning');

warning(
  2 + 2 === 4,
  'Les maths sont en vacances aujourd’hui.'
);

L’avertissement est affiché lorsque la condition de warning est false.

Pensez-y en vous disant que la condition devrait refléter la situation normale plutôt que la situation exceptionnelle.

Ce serait plutôt bien d’éviter de spammer la console avec des avertissements en double :

var warning = require('warning');

var didWarnAboutMath = false;
if (!didWarnAboutMath) {
  warning(
    2 + 2 === 4,
    'Les maths sont en vacances aujourd’hui.'
  );
  didWarnAboutMath = true;
}

Les avertissements ne sont activés que dans la phase de développement. En production, ils sont complètement retirés du code. Si vous avez besoin d’interdire l’exécution d’une partie de code, utilisez plutôt le module invariant :

var invariant = require('invariant');

invariant(
  2 + 2 === 4,
  'Vous ne passerez pas !'
);

L’invariant est levé lorsque la condition de invariant est false.

Le terme « invariant » signifie simplement « cette condition est toujours vraie ». Vous pouvez voir ça comme une affirmation.

Pour les invariants, il est important d’avoir un comportement similaire en développement et en production, afin qu’ils soient levés dans les deux cas. Les messages d’erreur sont automatiquement remplacés par des codes d’erreur en production afin d’éviter toute incidence négative sur la taille (en octets) du fichier.

Développement et production

Vous pouvez utiliser la variable pseudo-globale __DEV__ dans le code source pour délimiter les blocs de code réservés au développement.

La variable est remplacée lors de la compilation et se transforme en contrôles process.env.NODE_ENV !== 'production' dans les builds CommonJS.

Pour les versions autonomes, la variable devient true dans la version non-minifiée du fichier produit, alors qu’elle est complètement effacée, ainsi que les blocs if qu’elle contrôle, dans la version minifiée.

if (__DEV__) {
  // Ce code va uniquement s’appliquer pendant le développement.
}

Flow

Nous avons récemment commencé à introduire des contrôles Flow dans le code source. Les fichiers marqués avec l’annotation @flow dans le commentaire d’en-tête de licence sont soumis à vérification.

Nous acceptons les pull requests qui ajoutent des annotations Flow au code existant. Les annotations Flow ressemblent à ceci :

ReactRef.detachRefs = function(
  instance: ReactInstance,
  element: ReactElement | string | number | null | false,
): void {
  // ...
}

Dans la mesure du possible, le nouveau code devrait utiliser des annotations Flow. Vous pouvez exécuter yarn flow localement pour vérifier votre code avec Flow.

Injection dynamique

React utilise l’injection dynamique dans certains modules. Bien que ce soit toujours explicite, c’est quand même dommage car ça nuit à la compréhension du code. Ces injections viennent principalement du fait que React ne visait initialement que le DOM. React Native a commencé comme un fork de React. Nous avons dû ajouter une injection dynamique pour permettre à React Native de remplacer certains comportements.

Vous verrez peut-être des modules déclarer leurs dépendances dynamiques comme ceci :

// Dynamically injected
var textComponentClass = null;

// Relies on dynamically injected value
function createInstanceForText(text) {
  return new textComponentClass(text);
}

var ReactHostComponent = {
  createInstanceForText,

  // Provides an opportunity for dynamic injection
  injection: {
    injectTextComponentClass: function(componentClass) {
      textComponentClass = componentClass;
    },
  },
};

module.exports = ReactHostComponent;

Le champ injection n’est en aucun cas traité spécialement. Mais par convention, il signifie que ce module veut recevoir certaines dépendances (supposément spécifiques à une plate-forme) par injection au moment de l’exécution.

Il y a plusieurs points d’injection dans le code source. À l’avenir, nous entendons nous débarrasser du mécanisme d’injection dynamique et raccorder toutes les pièces de manière statique pendant la construction.

Plusieurs paquets

React est un monorepo. Son dépôt contient plusieurs paquets distincts afin que leurs modifications puissent être coordonnées et que les problèmes puissent être signalés dans un seul et même endroit.

Le noyau de React

Le « noyau » de React inclut toutes les API React de niveau racine, par exemple :

  • React.createElement()
  • React.Component
  • React.Children

Le noyau React n’inclut que les API nécessaires à la définition des composants. Il n’inclut pas l’algorithme de réconciliation ni aucun code spécifique à une plate-forme. Il est utilisé à la fois par les composants de React DOM et de React Native.

Le code pour le noyau React se trouve dans packages/react au sein de l’arborescence source. Il est disponible sur npm via le module react. La version autonome correspondante pour l’utilisation à même le navigateur est appelée react.js, et exporte une variable globale appelée React.

Moteurs de rendu

React a été créé à l’origine pour le DOM, mais il a ensuite été adapté pour prendre également en charge les plates-formes natives avec React Native. C’est ainsi qu’est né le concept de « moteurs de rendu » (renderers, terme que nous utiliserons sans italiques dans la suite de ce texte, NdT) au sein de React.

Les renderers gèrent la transformation d’une arborescence React en appels à la plate-forme sous-jacente.

Les renderers sont également situés dans packages/ :

Le seul autre moteur de rendu officiellement pris en charge est react-art. Auparavant, il se trouvait dans un dépôt GitHub séparé, mais nous l’avons déplacé dans l’arborescence source principale pour le moment.

Remarque

Techniquement, le react-native-renderer est une couche très mince qui apprend à React à interagir avec l’implémentation de React Native. Le véritable code spécifique à la plate-forme, qui gère les vues natives et fournit les composants, réside quant à lui dans le dépôt React Native.

Réconciliateurs

Même des moteurs de rendu très différents comme React DOM et React Native doivent partager beaucoup de logique. En particulier, l’algorithme de réconciliation doit être aussi similaire que possible afin que le rendu déclaratif, les composants personnalisés, l’état local, les méthodes de cycle de vie et les refs fonctionnent de manière cohérente sur toutes les plates-formes prises en charge.

Pour résoudre ce problème, différents moteurs de rendu partagent du code entre eux. Nous appelons cette partie de React un « réconciliateur ». Lorsqu’une mise à jour telle que setState() est planifiée, le réconciliateur appelle render() sur les composants de l’arborescence et les monte, les met à jour ou les démonte.

Les réconciliateurs ne font pas l’objet de modules séparés, car ils ne disposent actuellement d’aucune API publique. Ils sont exclusivement utilisés par les moteurs de rendu tels que React DOM et React Native.

Réconciliateur Stack

Le réconciliateur “stack” est l’implémentation qui sous-tend React 15 et les versions antérieures. Nous avons depuis cessé de l’utiliser, mais il reste décrit en détail dans la prochaine page.

Réconciliateur Fiber

Le réconciliateur “fiber” représente une nouvelle tentative de résoudre les problèmes inhérents au réconciliateur “stack” en plus de quelques problèmes anciens. C’est le réconciliateur par défaut depuis React 16.

Ses objectifs principaux sont :

  • la capacité à diviser un travail interruptible en segments ;
  • la capacité à hiérarchiser, déplacer et réutiliser des travaux en cours ;
  • la capacité à jongler entre parents et enfants pour exécuter une mise en page avec React ;
  • la capacité à renvoyer plusieurs éléments depuis render() ;
  • une meilleure prise en charge des périmètres d’erreur.

Vous pouvez en apprendre davantage sur l’architecture React Fiber ici et ici. Bien qu’elles soient livrées avec React 16, les fonctionnalités asynchrones ne sont pas encore activées par défaut.

Son code source est situé dans packages/react-reconciler.

Système d’événements

React implémente un système d’événements synthétiques indépendant du moteur de rendu, qui fonctionne à la fois avec React DOM et React Native. Son code source se trouve dans packages/events.

Voici une vidéo qui plonge en profondeur dans ce code (66 minutes).

Et maintenant ?

Lisez la prochaine page pour en apprendre davantage sur l’implémentation du réconciliateur utilisé avant React 16. Nous n’avons pas encore documenté les détails internes d’implémentation du nouveau réconciliateur.