React & BEM - collaboration officielle. Partie historique

Voici l'histoire de l'intégration de la méthodologie BEM dans l'univers React. Le matériel que vous lirez est basé sur l'expérience des développeurs Yandex développant le service le plus important et le plus chargé en Russie - Yandex.Search. Nous n'avions jamais parlé auparavant de maniÚre aussi détaillée et approfondie des raisons pour lesquelles nous l'avions fait, et non autrement, de ce qui nous motivait et de ce que nous voulions vraiment. L'étranger a obtenu des versions sÚches et des critiques lors de conférences. Ce n'est qu'en marge qu'on pouvait entendre quelque chose comme ça. En tant que co-auteur, j'étais indigné à cause de la rareté des informations à l'extérieur chaque fois que je parlais de nouvelles versions de bibliothÚques. Mais cette fois, nous partagerons tous les détails.



Tout le monde a entendu parler de la méthodologie BEM. Sélecteurs CSS avec traits de soulignement. L'approche des composants a été évoquée, en tenant compte de la façon dont les sélecteurs CSS CSS sont écrits. Mais il n'y aura pas un mot sur CSS dans l'article. Seulement JS, seulement hardcore!


Pour comprendre pourquoi la méthodologie est apparue et quels problÚmes Yandex a rencontrés à ce moment-là, je vous recommande de vous familiariser avec l'histoire de BEM.


Prologue


BEM est vraiment né comme un salut d'une forte connectivité et de l'imbrication dans CSS. Mais la division de la feuille style.css en fichiers pour chaque bloc, élément ou modificateur a inévitablement conduit à une structuration similaire du code JavaScript.


En 2011, Open Source a acquis les premiers commits du i-bem.js , qui fonctionnait en conjonction avec bem-xjst moteur de template bem-xjst . Les deux technologies sont issues du XSLT et ont servi l'idée alors populaire de séparer la logique métier et la présentation des composants. Dans le monde extérieur, ce furent les grands moments du guidon et du Underscore.


bem-xjst est un type de moteur de modÚle différent. Afin d'approfondir mes connaissances sur l'architecture des approches de normalisation, je recommande fortement le rapport de Sergei Berezhnoy . bem-xjst pouvez essayer le moteur de modÚle bem-xjst dans le bac à sable en ligne .


En raison des spĂ©cificitĂ©s des services de recherche Yandex, les interfaces utilisateur sont construites Ă  l'aide de donnĂ©es. La page des rĂ©sultats de la recherche est unique pour chaque requĂȘte.



RequĂȘte de recherche par rĂ©fĂ©rence



RequĂȘte de recherche par rĂ©fĂ©rence



RequĂȘte de recherche par rĂ©fĂ©rence


Lorsque la division en un bloc, un élément et un modificateur s'est propagée au systÚme de fichiers, cela n'a permis de collecter autant que possible que le code nécessaire, en fait pour chaque page, pour chaque demande de l'utilisateur. Mais comment?


 src/components ├── ComponentName │ ├── _modName │ │ ├── ComponentName_modName.tsx —   │ │ └── ComponentName_modName_modVal.tsx —    │ ├── ElementName │ │ └── ComponentName-ElementName.tsx —   ComponentName │ ├── ComponentName.i18n —   │ │ ├── ru.ts —     │ │ ├── en.ts —     │ │ └── index.ts —    │ ├── ComponentName.test —   │ │ ├── ComponentName.page-object.js — Page Object │ │ ├── ComponentName.hermione.js —   │ │ └── ComponentName.test.tsx — unit- │ ├── ComponentName.tsx —    │ ├── ComponentName.scss —   │ ├── ComponentName.examples.tsx —    Storybook │ └── README.md —   

Structure de répertoire de composants moderne


Comme dans certaines autres sociĂ©tĂ©s, chez Yandex, les dĂ©veloppeurs d'interfaces sont responsables du frontend, qui se compose de la partie client dans le navigateur et de la partie serveur sur Node.js La partie serveur traite les donnĂ©es de la "grande" recherche et leur impose des modĂšles. Le traitement des donnĂ©es primaires convertit JSON en BEMJSON , la structure de donnĂ©es bem-xjst moteur de modĂšle bem-xjst . Le moteur de modĂšle contourne chaque nƓud de l'arborescence et lui impose un modĂšle. Étant donnĂ© que la conversion principale a lieu sur le serveur et, en raison de la division en petites entitĂ©s, les nƓuds correspondent aux fichiers, lors de la gĂ©nĂ©ration du modĂšle, nous poussons le code du navigateur qui sera utilisĂ© uniquement sur la page actuelle.


Vous trouverez ci-dessous la correspondance des nƓuds BEMJSON avec les fichiers du systùme de fichiers.


 module.exports = { block: 'Select', elem: 'Item', elemMods: { type: 'navigation' } }; 

 src/components ├── Select │ ├── Item │ │ _type │ │ ├── Select-Item_type_navigation.js │ │ └── Select-Item_type_navigation.css 

Le systĂšme modulaire YModules Ă©tait chargĂ© d'isoler les composants du code JavaScript dans le navigateur. Il vous permet de fournir des modules de maniĂšre synchrone et asynchrone au navigateur. Un exemple du fonctionnement des composants avec YModules et i-bem.js peut ĂȘtre trouvĂ© ici . Aujourd'hui, pour la plupart des dĂ©veloppeurs, le webpack et la norme inĂ©dite d'importations dynamiques le font .


Un ensemble de méthodologie BEM, de moteur de modÚle déclaratif et de framework JS avec un systÚme modulaire a permis de résoudre n'importe quel problÚme. Mais au fil du temps, la dynamique est venue aux interfaces utilisateur.


Un nouvel espoir


En 2013, React a enchanté l' Open Source. En fait, Facebook a commencé à l'utiliser en 2011. James Long, dans ses notes de la conférence JS Conf US , dit:


Les deux derniÚres séances ont été une surprise. Le premier a été donné par deux développeurs Facebook et ils ont annoncé Facebook React . Je n'ai pas pris beaucoup de notes parce que j'étais un peu choqué de voir à quel point je pense que c'est une mauvaise idée. Essentiellement, ils ont créé un langage appelé JSX qui vous permet d'incorporer XML dans JavaScript pour créer des interfaces utilisateur réactives en direct. XML En JavaScript.

React a changé l'approche de la conception d'applications Web. Il est devenu si populaire qu'aujourd'hui vous ne pouvez pas trouver un développeur qui n'a pas entendu parler de React. Mais une autre chose est importante: les applications sont devenues différentes, le SPA est entré dans nos vies.


Il est généralement admis que les développeurs Yandex ont un sens particulier de la beauté en ce qui concerne la technologie. Parfois étrange, ce qui est difficile à discuter, mais jamais sans raison. Lorsque React a marqué des étoiles sur GitHub , beaucoup de ceux qui connaissaient les technologies Web Yandex ont insisté: Facebook a gagné, abandonnez votre artisanat et exécutez tout réécrire sur React avant qu'il ne soit trop tard. Il est important de comprendre deux choses.


PremiĂšrement, il n'y a pas eu de guerre. Les entreprises ne rivalisent pas pour crĂ©er le meilleur cadre sur Terre. Si une entreprise commence Ă  consacrer moins de temps (lecture - argent) Ă  des tĂąches d'infrastructure avec la mĂȘme productivitĂ©, tout le monde en bĂ©nĂ©ficiera. Cela n'a aucun sens d'Ă©crire des frameworks pour Ă©crire des frameworks. Les meilleurs dĂ©veloppeurs crĂ©ent des outils qui rĂ©solvent au mieux les tĂąches de l'entreprise. Entreprises, services, objectifs - tout est diffĂ©rent. D'oĂč la variĂ©tĂ© des outils.


DeuxiÚmement, nous recherchions un moyen d'utiliser React comme nous le souhaiterions. Avec toutes les fonctionnalités offertes par nos technologies décrites ci-dessus.


Il est largement admis que le code utilisant React est rapide par défaut. Si vous le pensez aussi, vous vous trompez profondément. La seule chose que React fait, dans la plupart des cas, aide à interagir de maniÚre optimale avec le DOM.


Jusqu'Ă  la version 16, React avait une faille fatale. Il Ă©tait 10 fois plus lent que bem-xjst sur le serveur. Nous ne pouvions pas nous permettre de tels dĂ©chets. Le temps de rĂ©ponse pour Yandex est l'une des mesures clĂ©s. Imaginez que lorsque vous demandez une recette de vin chaud, vous obtenez une rĂ©ponse 10 fois plus lente que d'habitude. Vous ne serez pas satisfait des excuses, mĂȘme si vous savez quoi que ce soit sur le dĂ©veloppement Web. Que pouvons-nous dire de l'explication, comme "mais il est devenu plus pratique pour les dĂ©veloppeurs de communiquer avec le DOM". Ajoutez ici le ratio du prix de la mise en Ɠuvre et du profit - et vous prendrez vous-mĂȘme la seule bonne dĂ©cision.


Heureusement pour le chagrin, les développeurs sont des gens étranges. Si quelque chose ne fonctionne pas, ce n'est pas une raison pour tout laisser tomber ...


À l'envers


Nous étions convaincus que nous pourrions vaincre la lenteur de React. Nous avons déjà un moteur de modÚle rapide. Tout ce dont vous avez besoin est de générer du HTML sur le serveur en utilisant bem-xjst , et sur le client pour "forcer" React à accepter ce balisage comme le sien. L'idée était si simple que rien n'annonçait un échec.


Dans les versions jusqu'Ă  15 inclusivement, React a validĂ© la validitĂ© du balisage Ă  l'aide d'une somme de hachage - un algorithme qui transforme toute optimisation en citrouille. Pour convaincre React de la validitĂ© du balisage, il a fallu noter un identifiant pour chaque nƓud et calculer la somme de hachage de tous les nƓuds. Cela signifiait Ă©galement prendre en charge un double ensemble de modĂšles: React pour le client et bem-xjst pour le serveur. Des tests de vitesse simples avec une installation id ont clairement montrĂ© qu'il Ă©tait inutile de continuer.


Le bem-xjst bem bem-xjst est un outil trĂšs sous-estimĂ©. Regardez le rapport du principal responsable de Glory Oliyanchuk et voyez par vous-mĂȘme. bem-xjst est basĂ© sur une architecture qui vous permet d'utiliser une syntaxe de modĂšle pour diffĂ©rentes transformations de l'arborescence source. TrĂšs similaire Ă  React, n'est-ce pas? Cette fonctionnalitĂ© permet aujourd'hui Ă  des outils tels que react-sketchapp .


bem-xjst contient deux types de conversions: en HTML et en JSON. Tout développeur suffisamment diligent peut écrire son propre moteur pour transformer des modÚles en n'importe quoi. Nous avons appris à bem-xjst transformer un arbre de données en une séquence d'appels aux fonctions HyperScript . Ce qui signifiait une compatibilité totale avec React et d'autres implémentations de l'algorithme Virtual DOM, par exemple, Preact .



Une introduction détaillée à la génération d'appels de fonction HyperScript


Comme les modÚles React nécessitent la coexistence de la mise en page et de la logique métier, nous avons dû intégrer la logique d' i-bem.js dans nos modÚles, qui n'étaient pas destinés à cela. Pour eux, ce n'était pas naturel. Ils allaient différemment. Au fait!


Ci-dessous est un exemple des profondeurs de collage de différents mondes en un seul runtime.


 block('select').elem('menu')( def()(function() { const React = require('react'); const Menu = require('../components/menu/menu'); const MenuItem = require('../components/menu-item/menu-item'); const _select = this.ctx._select; const selectComponent = _select._select; return React.createElement.apply(React, [ Menu, { mix: { block : this.block, elem : this.elem }, ref: menu => selectComponent._menu = menu, size: _select.mods.size, disabled: _select.mods.disabled, mode: _select.mods.mode, content: _select.options, checkedItems: _select.bindings.checkedItems, style: _select.bindings.popupMenuWidth, onKeyDown: _select.bindings.onKeyDown, theme: _select.mods.theme, }].concat(_select.options.map(option => React.createElement( MenuItem, { onClick: _select.bindings.onOptionCheck, theme: _select.mods.theme, val: option.value, }, option.content) )) ); }) ); 

Bien sĂ»r, nous avions notre propre assemblĂ©e. Comme vous le savez, l'opĂ©ration la plus rapide est la concatĂ©nation de chaĂźnes. Le moteur bem-xjst a Ă©tĂ© construit dessus, l'ensemble a Ă©tĂ© construit dessus. Les fichiers de blocs, d'Ă©lĂ©ments et de modificateurs se trouvaient dans des dossiers, et l'assemblage n'avait qu'Ă  coller les fichiers dans le bon ordre. Avec cette approche, vous pouvez coller JS, CSS et modĂšles en parallĂšle, ainsi que les entitĂ©s elles-mĂȘmes. Autrement dit, si vous avez quatre composants dans un projet, quatre cƓurs sur l'ordinateur portable et que l'assemblage d'une technologie de composant prend une seconde, la construction du projet prendra deux secondes. Ici, il devrait devenir plus clair comment nous parvenons Ă  insĂ©rer uniquement le code nĂ©cessaire dans le navigateur.


Tout cela pour nous a fait ENB . Nous avons reçu l'arborescence finale pour la normalisation uniquement lors de l'exécution, et comme la dépendance entre les composants devait survenir un peu plus tÎt pour collecter les bundles, cette fonction a été reprise par la technologie deps.js peu connue. Il vous a permis de créer un graphique de dépendance entre les composants, aprÚs quoi le collecteur pouvait coller le code dans la séquence souhaitée, en contournant le graphique.


React version 16 a cessé de fonctionner dans ce sens. La vitesse d'exécution des modÚles sur le serveur était égale . Sur les sites de production, la différence est devenue imperceptible.


Noeud: v8.4.0
Enfants: 5K


moteur de rendutemps moyenops / sec
pré-agir v8.2.666.235ms15
bem-xjst v8.8.471.326ms14
réagir v16.1.073,966 ms14

En utilisant les liens ci-dessous, vous pouvez restaurer l'historique de l'approche:



Avons-nous essayé autre chose?




La motivation


Au milieu de l'histoire, il sera utile de parler de ce qui nous a motivĂ©s. Cela valait la peine de le faire au dĂ©but, mais - qui se souvient de l'ancien, cet Ɠil en cadeau. Pourquoi avons-nous besoin de tout cela? Que peut apporter BEM que React ne peut pas faire? Des questions que presque tout le monde pose.


Décomposition


La fonctionnalité des composants se complique d'année en année et le nombre de variations augmente. Cela s'exprime par des constructions if ou switch , par conséquent, la base de code augmente inévitablement, en conséquence, le poids du composant et le projet utilisant un tel composant augmentent. La partie principale de la logique du composant React est contenue dans la méthode render() . Pour modifier la fonctionnalité d'un composant, il est nécessaire de réécrire la majeure partie de la méthode, ce qui conduit inévitablement à une augmentation exponentielle du nombre de composants hautement spécialisés.


Tout le monde connaĂźt les bibliothĂšques material-ui , fabric-ui et react-bootstrap . En gĂ©nĂ©ral, toutes les bibliothĂšques bien connues avec des composants ont le mĂȘme inconvĂ©nient. Imaginez que vous avez plusieurs projets et que vous utilisez tous la mĂȘme bibliothĂšque. Vous prenez les mĂȘmes composants, mais dans diffĂ©rentes variantes: ici il y a des sĂ©lections avec des cases Ă  cocher, il n'y en a pas, il y a des boutons bleus avec une icĂŽne, il y a des boutons rouges sans. Le poids de CSS et JS que la bibliothĂšque vous apporte sera le mĂȘme dans tous les projets. Mais pourquoi? Des variations de composants sont incorporĂ©es Ă  l'intĂ©rieur du composant lui-mĂȘme et sont fournies avec lui, que vous le vouliez ou non. Pour nous, c'est inacceptable.


Yandex possĂšde Ă©galement sa propre bibliothĂšque de composants - Lego. Il est appliquĂ© dans environ 200 services. Voulons-nous que l'utilisation de Lego dans la recherche coĂ»te le mĂȘme prix pour Yandex.Health? Vous connaissez la rĂ©ponse.


Développement multiplateforme


Pour prendre en charge plusieurs plates-formes, le plus souvent, elles créent soit une version distincte pour chaque plate-forme, soit une version adaptative.


Le développement de versions individuelles nécessite des ressources supplémentaires: plus il y a de plateformes, plus il faut d'efforts. Le maintien de l'état synchrone des propriétés du produit dans différentes versions entraßnera de nouvelles difficultés.


Le développement d'une version adaptative complique le code, augmente le poids, réduit la vitesse du produit avec la bonne différence entre les plateformes.


Voulons-nous que nos parents / amis / collÚgues / enfants utilisent des versions de bureau sur mobile avec une vitesse Internet et une productivité inférieures? Vous connaissez la réponse.


Les expériences


Si vous dĂ©veloppez des projets pour un large public, vous devez ĂȘtre sĂ»r de chaque changement. Les expĂ©riences A / B sont un moyen de gagner cette confiance.


Façons d'organiser le code des expériences:


  • fork du projet et crĂ©ation d'instances de service en production;
  • conditions ponctuelles Ă  l'intĂ©rieur de la base de code.

Si le projet comporte de nombreuses expériences longues, la ramification de la base de code entraßne des coûts importants. Il est nécessaire de tenir à jour chaque branche avec l'expérience: erreurs corrigées de port et fonctionnalité du produit. La ramification de la base de code complique les expériences qui se croisent plusieurs fois.


Les conditions ponctuelles fonctionnent de maniÚre plus flexible, mais compliquent la base de code: les conditions de l'expérience peuvent affecter différentes parties du projet. Un grand nombre de conditions dégradent les performances en augmentant la quantité de code pour le navigateur. Il est nécessaire de supprimer les conditions, de rendre le code basique ou de supprimer complÚtement l'expérience ayant échoué.


Dans la recherche ~ 100 expĂ©riences en ligne dans diffĂ©rentes combinaisons pour diffĂ©rents publics. Vous pouvez le voir par vous-mĂȘme. Rappelez-vous, vous avez peut-ĂȘtre remarquĂ© la fonctionnalitĂ©, et une semaine plus tard, elle a disparu comme par magie. Voulons-nous tester les thĂ©ories des produits au prix du maintien de centaines de branches de la base de code active de 500 000 lignes, qui sont modifiĂ©es quotidiennement par environ 60 dĂ©veloppeurs? Vous connaissez la rĂ©ponse.


Changement global


Par exemple, vous pouvez crĂ©er un composant CustomButton hĂ©ritĂ© de Button partir d'une bibliothĂšque. Mais le CustomButton hĂ©ritĂ© ne s'appliquera pas Ă  tous les composants de la bibliothĂšque contenant Button . Une bibliothĂšque peut avoir un composant de Search construit Ă  partir de l' Input et du Button . Dans ce cas, le CustomButton hĂ©ritĂ© n'apparaĂźt pas Ă  l'intĂ©rieur du composant de Search . Voulons-nous parcourir manuellement toute la base de code oĂč Button utilisĂ©?



Un long chemin vers la composition


Nous avons décidé de changer de stratégie. Dans l'approche précédente, ils ont pris la technologie Yandex comme base et ont essayé de faire fonctionner React sur cette base. De nouvelles tactiques suggÚrent le contraire. C'est ainsi qu'est né le projet bem-react-core .


ArrĂȘte ça! Pourquoi rĂ©agir du tout?

Nous y avons vu une opportunité de se débarrasser du rendu initial explicite en HTML et de la prise en charge manuelle de l'état du composant JS plus tard lors de l'exécution - en fait, il est devenu possible de fusionner des modÚles BEMHMTL et des composants JS en une seule technologie.


v1.0.0


Au départ, nous avions prévu de transférer toutes les meilleures pratiques et propriétés bem-xjst à la bibliothÚque en plus de React. La premiÚre chose qui attire votre attention est la signature ou, si vous préférez, la syntaxe pour décrire les composants.


Qu'avez-vous fait, il y a JSX!


La premiÚre version a été construite sur la base de l' héritage - une bibliothÚque qui aide à implémenter les classes et l'héritage. Comme certains d'entre vous s'en souviennent, à l'époque, les prototypes de prototypes en JavaScript n'avaient pas de classes, il n'y avait pas de super . En général, ils sont encore absents, plus précisément, ce ne sont pas les classes qui viennent à l'esprit en premier. inherit fait tout ce que les classes de la norme ES2015 peuvent faire maintenant, et ce qui est considéré comme de la magie noire: l'héritage multiple et la fusion du prototype au lieu de reconstruire la chaßne, ce qui affecte positivement les performances. Vous ne vous tromperez pas si vous pensez que cela semble logique comme hérite dans Node.js , mais ils fonctionnent différemment.


Voici un exemple de la syntaxe des modĂšles bem-react-core@v1.0.0 .


App-Header.js


 import { decl } from 'bem-react-core'; export default decl({ block: 'App', elem: 'Header', attrs: { role: 'heading' }, content() { return ' '; } }); 

App-Header@desktop.js


 import { decl } from 'bem-react-core'; export default decl({ block: 'App', elem: 'Header', tag: 'h1', attrs() { return { ...this.__base(...arguments), 'aria-level': 1 }, }, content() { return ` ${this.__base(...arguments)}     h1`; } }); 

App-Header@touch.js


 import { decl } from 'bem-react-core'; export default decl({ block: 'App', elem: 'Header', tag: 'h2', content() { return ` ${this.__base(...arguments)}  `; } }); 

index.js


 import ReactDomServer from 'react-dom/server'; import AppHeader from 'b:App e:Header'; ReactDomServer.renderToStaticMarkup(<AppHeader />); 

output@desktop.html


 <h1 class="App-Header" role="heading" aria-level="1">A       h1</h2> 

output@touch.html


 <h2 class="App-Header" role="heading">   </h2> 

Les modĂšles d'appareils pour les composants plus complexes peuvent ĂȘtre trouvĂ©s ici .


Puisqu'une classe est un objet et qu'il est plus pratique de travailler avec des objets en JavaScript, la syntaxe est appropriée. La syntaxe a ensuite migré vers son cerveau bem-xjst .


La bibliothÚque était un référentiel global de déclarations d'objets - les résultats de l'exécution de la fonction decl , des parties d'entités: un bloc, un élément ou un modificateur. BEM fournit un mécanisme de dénomination unique et convient donc à la création de clés dans un coffre-fort. Le composant React résultant a été collé sur son lieu d'utilisation. L'astuce est que decl fonctionné lors de l'importation du module. Cela a permis d'indiquer quelles parties du composant sont nécessaires à chaque endroit particulier à l'aide d'une simple liste d'importations. Mais rappelez-vous: les composants sont complexes, il y a de nombreuses parties, la liste des importations est longue, les développeurs sont paresseux.


Importer de la magie


Comme vous pouvez le voir, dans les exemples de code, des lignes import AppHeader from 'b:App e:Header' .


Vous avez brisĂ© la norme! C’est impossible! Ça ne marchera tout simplement pas!


PremiÚrement, la norme d'importation ne fonctionne pas avec des termes dans l'esprit «il doit y avoir un chemin vers un module réel dans la ligne d'importation». DeuxiÚmement, c'est le sucre syntaxique qui a été converti à l'aide de Babel. TroisiÚmement, import txt from 'raw-loader!./file.txt'; constructions de ponctuation d' import txt from 'raw-loader!./file.txt'; bizarres pour le webpack import txt from 'raw-loader!./file.txt'; pour une raison quelconque, ils n'ont dérangé personne.
Ainsi, notre bloc est présenté sur deux plateformes: desktop , touch .


 import Hello from 'b:Hello'; //     : var Hello = [ require('path/to/desktop/Hello/Hello.js'), require('path/to/touch/Hello/Hello.js') ][0].applyDecls(); 

Ici, le code importera séquentiellement toutes les définitions de composants Hello, puis appellera une fonction applyDeclsqui colle toutes les déclarations de bloc du référentiel global inheritet crée un nouveau composant React unique à un endroit spécifique du projet.


Un plugin pour Babel qui effectue cette conversion peut ĂȘtre trouvĂ© ici . Et le chargeur pour webpack, qui cherchait des dĂ©finitions de composants sur le systĂšme de fichiers, est ici .


Au final, ce qui était bien:


  • une syntaxe brĂšve et dĂ©clarative des modĂšles, qui vous permet de redĂ©finir diffĂ©rentes parties du composant n'importe oĂč dans le projet;
  • pas de chaĂźnes prototypes en hĂ©ritage;
  • Composant React unique pour chaque lieu d'utilisation.

Et c'était mauvais:


  • TypeScript/Flow;
  • React- ;
  • - ;
  • .

v2.0.0


bem-react-core@v1.0.0 , .


 import { Elem } from 'bem-react-core'; import { Button } from '../Button'; export class AppHeader extends Elem { block = 'App'; elem = 'Header'; tag() { return 'h2'; } content() { return ( <Button> </Button> ); } } 

, . , , TypeScript/Flow. , inherit «» , , .


:
— webpack Babel;
— ;
— , .


HOC , .


 import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Block, Elem, withMods } from 'bem-react-core'; interface IButtonProps { children: string; } interface IModsProps extends IButtonProps { type: 'link' | 'button'; } //   Text class Text extends Elem { block = 'Button'; elem = 'Text'; tag() { return 'span'; } } //   Button class Button<T extends IModsProps> extends Block<T> { block = 'Button'; tag() { return 'button'; } mods() { return { type: this.props.type }; } content() { return ( <Text>{this.props.children}</Text> ); } } //    Button,    type   link class ButtonLink extends Button<IModsProps> { static mod = ({ type }: any) => type === 'link'; tag() { return 'a'; } mods() { return { type: this.props.type }; } attrs() { return { href: 'www.yandex.ru' }; } } //   Button  ButtonLink const ButtonView = withMods(Button, ButtonLink); ReactDOM.render( <React.Fragment> <ButtonView type='button'>Click me</ButtonView> <ButtonView type='link'>Click me</ButtonView> </React.Fragment>, document.getElementById('root') ); 

, .


withMods , (), . , , withMods , . . , , , ( ) . . , , — , .


, :


  • . , . , TS. , . ES5 TS super , . , TS , .
  • . TS ES6 Babel ES5. , npm- . , Babel.

:


  • , . , . : DOM-. HOC, . withMods .
  • (, , ) . SFC .
  • CSS-. CSS- JS- . , , .

v2.



, . . , , 1 2. .


— . CSS- HOC, — dependency injection .


React:


  • CSS-.
  • (, );

. . React.ComponentType -. HOC compose .


.


dependency injection, React.ContextAPI . , , . , . DI — HOC, . . , , .


, , . , , 4 , 1.5Kb .


. Merci Ă  ceux qui ont lu jusqu'au bout. , React . .

Source: https://habr.com/ru/post/fr438598/


All Articles