Back

SolidJS vs React : Comparaison des modèles de composants et des performances

SolidJS vs React : Comparaison des modèles de composants et des performances

Lors du choix d’un framework frontend, comprendre comment il gère le rendu des composants et les performances est crucial. Pour les développeurs React qui considèrent SolidJS, les différences dans les modèles de composants et les systèmes de réactivité impactent directement les performances de l’application et l’expérience de développement.

Cet article compare les modèles de composants de SolidJS et React, en se concentrant sur leurs mécanismes de rendu, leurs caractéristiques de performance et les différences pratiques de code.

Points clés à retenir

  • React utilise un modèle basé sur le rendu où les composants se ré-exécutent à chaque changement d’état, tandis que les composants SolidJS s’exécutent une seule fois et créent des liaisons réactives
  • React emploie une réactivité à grain grossier avec un DOM virtuel, tandis que SolidJS utilise une réactivité à grain fin avec des mises à jour directes du DOM
  • SolidJS surpasse généralement React, surtout pour les applications avec des mises à jour fréquentes ou de gros volumes de données
  • React nécessite des techniques de mémorisation explicites, tandis que SolidJS nécessite moins d’optimisations grâce à son système réactif
  • SolidJS a une taille de bibliothèque principale significativement plus petite (~7KB) comparée à React (~40KB)

Modèles de composants : Différences fondamentales

React et SolidJS adoptent des approches fondamentalement différentes pour le rendu des composants, malgré leur syntaxe JSX similaire.

Modèle de composant de React

React utilise un modèle basé sur le rendu avec ces caractéristiques clés :

  • Les composants sont des fonctions qui s’exécutent à chaque changement d’état
  • Utilise un DOM virtuel pour minimiser les mises à jour réelles du DOM
  • S’appuie sur le diffing pour déterminer ce qui a changé
  • Les composants re-rendent par défaut tout leur sous-arbre
function Counter() {
  const [count, setCount] = useState(0);
  
  // Cette fonction s'exécute à chaque rendu
  console.log("Component rendering");
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Dans cet exemple, toute la fonction composant se ré-exécute chaque fois que count change, et le processus de réconciliation de React détermine quelles mises à jour du DOM sont nécessaires.

Modèle de composant de SolidJS

SolidJS utilise un modèle de compilation réactive avec ces caractéristiques :

  • Les composants s’exécutent seulement une fois pendant l’initialisation
  • Pas de DOM virtuel ou d’algorithme de diffing
  • Crée un graphe réactif de dépendances
  • Met à jour seulement les nœuds DOM spécifiques affectés par les changements d’état
function Counter() {
  const [count, setCount] = createSignal(0);
  
  // Cette fonction s'exécute seulement une fois
  console.log("Component initializing");
  
  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>Increment</button>
    </div>
  );
}

Dans SolidJS, la fonction composant s’exécute juste une fois. Le système réactif suit quelles parties du DOM dépendent de quels signaux, mettant à jour seulement ces nœuds spécifiques quand les valeurs changent.

Comparaison des systèmes de réactivité

Les systèmes de réactivité de React et SolidJS déterminent comment les changements d’état se propagent vers l’interface utilisateur.

Réactivité basée sur les hooks de React

React utilise un système de réactivité à grain grossier :

  • L’état est géré via des hooks comme useState et useReducer
  • Les changements d’état déclenchent le re-rendu des composants
  • React s’appuie sur la mémorisation (useMemo, useCallback, React.memo) pour éviter les re-rendus inutiles
  • Le flux de données est géré via les props et le contexte
function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: "Learn React", completed: false },
    { id: 2, text: "Build app", completed: false }
  ]);

  // Ce composant entier se re-rend quand todos change
  const toggleTodo = (id) => {
    setTodos(todos.map(todo => 
      todo.id === id ? {...todo, completed: !todo.completed} : todo
    ));
  };

  return (
    <ul>
      {todos.map(todo => (
        <TodoItem 
          key={todo.id} 
          todo={todo} 
          onToggle={() => toggleTodo(todo.id)} 
        />
      ))}
    </ul>
  );
}

// Ce composant se re-rend quand les props changent
function TodoItem({ todo, onToggle }) {
  console.log(`Rendering: ${todo.text}`);
  return (
    <li>
      <input 
        type="checkbox" 
        checked={todo.completed} 
        onChange={onToggle} 
      />
      <span>{todo.text}</span>
    </li>
  );
}

Réactivité à grain fin de SolidJS

SolidJS utilise un système de réactivité à grain fin :

  • L’état est géré via des primitives réactives comme createSignal et createStore
  • Les mises à jour sont granulaires, ciblant seulement les nœuds DOM affectés
  • Pas besoin de mémorisation extensive
  • Les dépendances réactives sont suivies automatiquement
function TodoList() {
  const [todos, setTodos] = createStore([
    { id: 1, text: "Learn SolidJS", completed: false },
    { id: 2, text: "Build app", completed: false }
  ]);

  const toggleTodo = (id) => {
    setTodos(id, "completed", completed => !completed);
  };

  return (
    <ul>
      <For each={todos}>
        {(todo) => (
          <TodoItem todo={todo} onToggle={[toggleTodo, todo.id]} />
        )}
      </For>
    </ul>
  );
}

function TodoItem(props) {
  // Ceci se log seulement une fois pendant l'initialisation
  console.log(`Creating: ${props.todo.text}`);
  
  return (
    <li>
      <input 
        type="checkbox" 
        checked={props.todo.completed} 
        onChange={() => props.onToggle[0](props.onToggle[1])} 
      />
      <span>{props.todo.text}</span>
    </li>
  );
}

Benchmarks et analyse des performances

Les différents modèles de composants conduisent à des différences significatives de performance entre React et SolidJS.

Performance de rendu

Selon le JS Framework Benchmark, SolidJS surpasse constamment React sur la plupart des métriques :

  • Rendu initial : SolidJS est typiquement 30-40% plus rapide
  • Performance de mise à jour : SolidJS peut être 2-5x plus rapide pour les mises à jour partielles
  • Utilisation mémoire : SolidJS utilise significativement moins de mémoire grâce à son approche compilée

Opérations DOM

La différence clé de performance provient de la façon dont les opérations DOM sont gérées :

  • React : Crée une représentation virtuelle du DOM, la compare avec la version précédente, puis applique les changements
  • SolidJS : Compile les composants en opérations DOM directes sans représentation intermédiaire

Cette différence devient plus prononcée avec :

  • De grandes listes d’éléments
  • Des mises à jour d’état fréquentes
  • Des arbres de composants complexes

Exemples de composants du monde réel

Examinons comment ces différences se manifestent dans un exemple pratique : une liste filtrable.

Implémentation React

function FilterableList() {
  const [items] = useState([
    "Apple", "Banana", "Cherry", "Date", "Elderberry"
  ]);
  const [filter, setFilter] = useState("");
  
  // Ce calcul s'exécute à chaque rendu
  const filteredItems = items.filter(item => 
    item.toLowerCase().includes(filter.toLowerCase())
  );
  
  return (
    <div>
      <input 
        type="text" 
        value={filter} 
        onChange={e => setFilter(e.target.value)} 
        placeholder="Filter items..." 
      />
      <ul>
        {filteredItems.map(item => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

Dans React, quand le filtre change :

  1. Le composant entier se re-rend
  2. La logique de filtrage se ré-exécute
  3. React compare la liste précédente et la nouvelle
  4. Seuls les nœuds DOM modifiés sont mis à jour

Implémentation SolidJS

function FilterableList() {
  const [items] = createSignal([
    "Apple", "Banana", "Cherry", "Date", "Elderberry"
  ]);
  const [filter, setFilter] = createSignal("");
  
  // Ceci crée un signal dérivé qui recalcule seulement quand les dépendances changent
  const filteredItems = createMemo(() => 
    items().filter(item => 
      item.toLowerCase().includes(filter().toLowerCase())
    )
  );
  
  return (
    <div>
      <input 
        type="text" 
        value={filter()} 
        onInput={e => setFilter(e.target.value)} 
        placeholder="Filter items..." 
      />
      <ul>
        <For each={filteredItems()}>
          {item => <li>{item}</li>}
        </For>
      </ul>
    </div>
  );
}

Dans SolidJS, quand le filtre change :

  1. Seul le signal filter se met à jour
  2. Le memo filteredItems recalcule
  3. Le composant For met à jour efficacement seulement les éléments de liste qui doivent changer
  4. Aucun re-rendu de composant n’a lieu

Utilisation mémoire et taille de bundle

Les modèles de composants affectent aussi l’utilisation mémoire et la taille de bundle :

Empreinte mémoire

  • React : Utilisation mémoire plus élevée due aux instances de composants, aux nœuds fiber et au DOM virtuel
  • SolidJS : Utilisation mémoire plus faible car les composants sont compilés après l’initialisation

Taille de bundle

  • React : La bibliothèque principale fait ~40KB (minifiée + gzippée)
  • SolidJS : La bibliothèque principale fait ~7KB (minifiée + gzippée)

Cette différence peut être significative pour les applications critiques en performance, surtout sur les appareils mobiles.

Techniques d’optimisation

Chaque framework nécessite différentes approches d’optimisation en raison de leurs modèles de composants.

Optimisation React

Les développeurs React doivent être familiers avec plusieurs techniques d’optimisation :

  • React.memo pour éviter les re-rendus inutiles de composants
  • useMemo pour mettre en cache les calculs coûteux
  • useCallback pour stabiliser les références de fonctions
  • Gestion d’état soigneuse pour éviter de re-rendre de gros arbres de composants
// Composant React optimisé
const ExpensiveComponent = React.memo(({ data, onAction }) => {
  // Logique du composant
});

function ParentComponent() {
  const [data, setData] = useState([]);
  
  // Stabiliser la référence de fonction
  const handleAction = useCallback((id) => {
    // Logique d'action
  }, []);
  
  // Mettre en cache le calcul coûteux
  const processedData = useMemo(() => {
    return data.map(item => expensiveProcess(item));
  }, [data]);
  
  return <ExpensiveComponent data={processedData} onAction={handleAction} />;
}

Optimisation SolidJS

SolidJS nécessite moins d’optimisations explicites :

  • createMemo pour mettre en cache les calculs coûteux
  • Utilisation appropriée des signaux pour assurer des mises à jour granulaires
// Composant SolidJS avec optimisation minimale nécessaire
function ParentComponent() {
  const [data, setData] = createSignal([]);
  
  // La fonction n'a pas besoin de stabilisation
  const handleAction = (id) => {
    // Logique d'action
  };
  
  // Mettre en cache le calcul coûteux
  const processedData = createMemo(() => {
    return data().map(item => expensiveProcess(item));
  });
  
  return <ExpensiveComponent data={processedData()} onAction={handleAction} />;
}

Conclusion

La différence fondamentale entre React et SolidJS réside dans leurs modèles de composants. L’approche basée sur le rendu de React fournit un modèle mental plus simple mais nécessite plus d’optimisation pour les applications critiques en performance. Le modèle de compilation réactive de SolidJS offre des performances supérieures avec moins d’effort d’optimisation mais nécessite de comprendre les concepts de programmation réactive.

Votre choix entre ces frameworks devrait considérer les exigences de performance de votre application, l’expertise de l’équipe et les besoins d’écosystème. React offre la maturité et un vaste écosystème, tandis que SolidJS fournit des avantages de performance et un modèle de réactivité plus efficace.

Les deux frameworks ont leurs forces, et comprendre leurs modèles de composants vous aide à prendre une décision éclairée pour votre prochain projet.

FAQ

Bien que SolidJS surpasse typiquement React dans les benchmarks, la performance du monde réel dépend des besoins spécifiques de votre application. React peut performer adéquatement pour de nombreuses applications, surtout avec une optimisation appropriée.

La transition nécessite d'apprendre le modèle de réactivité de SolidJS, mais la syntaxe JSX similaire rend la migration plus facile que vers d'autres frameworks. Cependant, vous devrez réécrire les composants pour utiliser les primitives réactives de SolidJS au lieu des hooks React.

SolidJS couvre la plupart des fonctionnalités principales de React mais a un écosystème plus petit. Il fournit des alternatives pour les fonctionnalités clés de React comme le contexte, les fragments, les portails et le suspense.

Choisissez React si vous avez besoin d'un écosystème mature, d'un support communautaire étendu, ou si vous construisez une grande application avec une équipe établie. Choisissez SolidJS si la performance est critique, vous construisez une application sensible à la performance, ou vous préférez un modèle de réactivité plus efficace.

SolidJS atteint de meilleures performances grâce à : 1) Une approche basée sur la compilation qui transforme les composants en opérations DOM efficaces, 2) Une réactivité à grain fin qui met à jour seulement ce qui a changé, 3) Pas de surcharge de DOM virtuel, 4) Un runtime minimal avec une empreinte mémoire plus petite.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers