Que choisir comme bibliothèque de composants pour un projet React

Je m'appelle Ksyusha Lugovaya. Chez SberCorus, je supporte la bibliothèque de composants Korus-UI React. 





Presque tous les développeurs sont tôt ou tard confrontés au problème du choix d'une bibliothèque, et parfois la solution peut être non triviale. Des questions se posent: sur quoi être guidé lors du choix d'une bibliothèque, quelles solutions populaires le marché propose-t-il, quels sont leurs avantages et leurs inconvénients? Les avis et témoignages ne vous aident pas toujours à trouver une solution.





Dans le monde du développement, il n'y a pas de solution parfaite pour toutes les situations. Par conséquent, dans l'article, je vais vous dire comment nous résolvons ce problème et analyser avec des exemples d'analyse de plusieurs solutions populaires comment choisir une bibliothèque de composants React pour votre projet. 





Les principaux critères de choix d'une bibliothèque

Scénarios d'utilisation de la bibliothèque. Cela semble évident, mais une compréhension claire des tâches est le principal critère de sélection.





Types de composants d'application. Le type d'application détermine les composants dont vous avez besoin. Souvent, un ensemble de boutons / cases à cocher, des champs de saisie de base, des listes / menus avec des styles prêts à l'emploi suffisent. Cela signifie que vous pouvez utiliser des composants simples avec un minimum de paramètres et de styles prêts à l'emploi.





Personnalisation, mise en forme et interactivité dans la conception. Si vous avez besoin de formater et de styliser vos composants de manière significative, il est également important de le décider à l'avance.





Lorsque les exigences sont clairement énoncées, répondez aux questions:





  • La documentation du projet est-elle bien rédigée, existe-t-il des exemples interactifs?





  • Dans quelle mesure le projet est-il activement soutenu?





  • Combien de problèmes y a-t-il dans le projet et à quelle vitesse sont-ils résolus?





  • Le projet est-il gratuit ou sous licence commerciale?





  • Dans quelle mesure les composants sont-ils faciles à personnaliser?





  • Le code de la bibliothèque est-il couvert par des tests?





  • Quels navigateurs et plates-formes la bibliothèque prend-elle en charge?





Ce sont des questions universelles pour vous aider à choisir une bibliothèque. Même si la solution fonctionnelle est idéale pour les besoins de votre projet, le manque de support ou un grand nombre de bugs non résolus est une bonne raison de choisir une autre bibliothèque. 





, :





  • Material-UI,





  • Semantic-UI-React,





  • yandex-ui,





  • arui-feather,





  • Korus-UI.





,   – Material-UI Semantic-UI-React, .





– , (, ) , opensource .





.





— , .





.





Layout- 





, , , . :





  • ;





  • props.children;





  • ;





  • :  h1, section, div, span, Icon, Avatar.





- ,   . , Material-UI   Semantic-UI   . -   .





- (controls)





, , , , — , «»  . 





:





  • ;





  • props;





  • UI (disabled, required, isLoading).









, . 





:





  • ;





  • layout- (, , );





  • .













Layout





Controls





Modules









Material-UI





App Bar, Avatars, Badges, Bottom Navigation, Divider, Grid List, Lists, Paper, Progress, Snackbar, Tables, 





Button, Chip, Selection Controls, Text Fields, Pickers*





Dialog, Cards, Drawers, ExpansionPanel, Menu, Stepper, Tabs, Tooltip





26**





Semantic-UI-React





Container, Divider, Flag, Header, Icon, Image, Label, List, Loader, Placeholder, Rail, Reveal, Segment, Step, Breadcrumb, Form, Grid, Menu, Message, Table, Advertisement, Card, Comment, Feed, Item, Statistic





Button, Input, Checkbox, Radio, Select, Text Area





Accordion, Dimmer, Dropdown, Embed, Modal, Popup, Progress, Rating, Search, Sidebar, Sticky, Tab, Transition, Visibility, Confirm, Pagination, Portal, Ref, Transitionable Portal





52





yandex-ui





Badge, Divider, Icon, Image, Text, UserPic, ListTile, Spacer, Link, Spin





Attach, Button, Checkbox, Menu, Radiobox, RadioButton, Select, Slider, Textarea, Textinput, Tumbler





TabsMenu, Drawer, Dropdown, Messagebox, Modal, Popup, TabsPanes, Tooltip, Progress 





30





arui-feather





Amount, CardImage, FlagIcon, Form, GridRow, GridCol, Heading, Icon, InputGroup, Label, Link, List, Paragraph, Spin





Attach, Button, CardInput, CheckBoxGroup, CheckBox, FormField, IconButton, Input, RadioGroup, Radio, Select, TagButton, Textarea, Toggle





CalendatInput, Calendar, Collapse, EmailInput, InputAutocomplete, IntlPhoneInput, Menu, MoneyInput, Notification, PhoneInput, Plate, Popup, ProgressBar, Sidebar, SlideDown, Tabs





44





Korus-UI





HTML tags factory***,





Currency, Tags





Button, Checkbox, Input,  Radio, Rating, Slider, Switcher, Textarea





Autocomplete, ButtonGroup, Collapse, Collapsible, 





DatePicker, DateRange, DateTimePicker, DateTimeRange, Dropdown, DropdownLink, DropdownSelect, Dropzone, FileDrop, FileUpload, Loader, MaskedInput, Modal, MultiSelect, Notifications, NumericRange, NumericTextBox, Pagination, Password, ProgressBar, StatusBar, StickyPanel, Tabs, TimePicker, TimeRange, Tooltip, Tour,  Validation, VStepper, Wizard, form





45





+ - HTML-





*Material-UI





** ,





***Korus-UI HTML- c API





50% Material-UI Semantic-UI-React 30%   yandex-ui arui-feather — layout-.  Korus-UI 70% — .





. .





Material-UI





  • MuiThemeProvider. React .





  • . className.





classes.





CSS-in-Js, , CSS-. CSS-in-Js HOC withStyles() makeStyles() .





Semantic-UI-React





Semantic-UI-React ,   Semantic-UI.





, . 





:





  • , site.variables;





  • css- ;





  • theme.config;





  • assets .





yandex-ui





. .





:





  • themekit





  • () ;





  • - yaml json. (css, json, js, ios, android) .





arui-feather ( )





.  className, .





Korus-UI ()





  • LedaProvider. React .





  • .     API- (.  theme). . 





. , ,   ( Loader ).





.





Material-UI





component.





, List <ul>



. React :





<List component="nav">
  <ListItem button>
    <ListItemText primary="Trash" />
  </ListItem>
  <ListItem button>
    <ListItemText primary="Spam" />
  </ListItem>
</List>
      
      







Semantic-UI





Semantic-UI-React as:





<Button as='a' />
      
      



React- . .





yandex-ui





render-override.    . 





:





import React from 'react'
import { useRenderOverride } from '@yandex/ui/lib/render-override'

const ElementOriginal = ({ children }) => <div>{children}</div>
const MyComponent = ({ renderElement }) => {
  const Element = useRenderOverride(ElementOriginal, renderElement)
  return (
    <>
      <Element />
    </>
  )
}
      
      







yandex-ui  .





arui-feather ( )





.





Korus-UI





API.   Render. , , .





:





labelRender={() => <MyCustomLabel />}
      
      



:





({ Element, elementProps, componentProps, componentState }) => React.Node
      
      



  • Element



    -





  • elementProps



    - props





  • componentState



    , componentProps



    - props state





 





, , :





<L.CheckBox
  labelRender={({ elementProps }) => <MyCustomLabel {…elementProps} />}
>
  Label
</L.CheckBox>
      
      



React- 2 :





  • Typescript





  • PropTypes





React Typescript.   , . PropTypes —   .





Typescript.  Semantic-UI JS, Typescript Semantic-UI-React, React.





—  . , .   , .   .





, — . .





( ) ( ).





.









 









%





Material-UI





Chai, Mocha, Sinon





Unit





95.28% Statements





87.22% Branches





97.51% Functions





95.26% Lines





Semantic-UI





Jasmine, Karma





Unit









Semantic-UI-React





Chai, Enzyme





Unit









yandex-ui





Jest, Enzyme





Unit









arui-feather





Jest, Enzyme





Unit





88.1% Statements





73.84% Branches





66.61% Functions





87.19% Lines





Korus-UI





Cypress, Jest





Unit, end-to-end





69.28% Statements





56.14% Branches





66.29% Functions





71.78% Lines





, . Storybook .

















Storybook





Material-UI





https://material-ui.com/ru/





-





-





Semantic-UI-React





https://react.semantic-ui.com/





+





-





yandex-ui





https://yastatic.net/s3/frontend/lego/storybook/index.html





-





+





arui-feather





https://digital.alfabank.ru/





+





-





Korus-UI





https://opensource.esphere.ru/korus-ui/





+





+





, . , .





. , , , ,   . : , . 





-.    , .  , ,   .





Pulse GitHub Pull Request .   Insights . .





Material-UI





Semantic-UI





yandex-ui





 arui-feather ( )





 Korus-UI ()





GitHub   npm-. ,     .   . 





, — SEO-, .   , , Stackoverflow, Medium, DEV.   issues . 





,   , . .

















,  % 





Material-UI





63 400





6 372 353





0,99





Semantic-UI





48 800





541 299





9





Semantic-UI-React





11 900





8 620 967





0,14





@yandex/ui





212





15 902





1,33





arui-feather ( )





559





26 744





2









. . , , , . , . 





: . , . 





, , ( ). (-), .





.





:   . , , , «».





Korus-UI





const BasicForm = () => (
  <L.Div>
    <L.Input
      isRequired
      requiredMessage="Login is required"
      form="form"
      name="login"
      placeholder="Login"
    />
    <L.Input
      isRequired
      requiredMessage="Password is required"
      form="form"
      name="password"
      placeholder="Password"
    />
    <L.Button _warning form="form">
      Submit
    </L.Button>
  </L.Div>
);
      
      







Material-UI





const BasicForm = () => {
  const [login, setLogin] = React.useState("");
  const [loginError, setLoginError] = React.useState(false);
  const [password, setPassword] = React.useState("");
  const [passwordError, setPasswordError] = React.useState(false);

  return (
    <div>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          setLoginError(!login);
          setPasswordError(!password);
        }}
      >
        <p>
          <TextField
            error={loginError}
            placeholder="Login"
            value={login}
            onChange={(e) => {
              setLoginError(false);
              setLogin(e.target.value);
            }}
            onBlur={(e) => {
              setLoginError(!login);
            }}
            helperText={loginError && "Login is required"}
          />
        </p>
        <p>
          <TextField
            error={passwordError}
            placeholder="Password"
            value={password}
            onChange={(e) => {
              setPasswordError(false);
              setPassword(e.target.value);
            }}
            onBlur={(e) => {
              setPasswordError(!password);
            }}
            helperText={passwordError && "Password is required"}
          />
        </p>
        <Button type="submit" color="primary" variant="contained">
          Sign Up
        </Button>
      </form>
    </div>
  );
};
      
      







Semantic-UI-React





const BasicForm = () => {
  const [login, setLogin] = React.useState("");
  const [loginError, setLoginError] = React.useState(false);
  const [password, setPassword] = React.useState("");
  const [passwordError, setPasswordError] = React.useState(false);

  return (
    <div>
      <Form
        onSubmit={(e) => {
          e.preventDefault();
          setLoginError(!login);
          setPasswordError(!password);
        }}
      >
        <Form.Group>
          <Form.Input
            error={loginError && { content: "Login is required" }}
            placeholder="Login"
            name="login"
            value={login}
            onChange={(e) => {
              setLoginError(false);
              setLogin(e.target.value);
            }}
            onBlur={(e) => {
              setLoginError(!login);
            }}
          />
          <Form.Input
            error={passwordError && { content: "Password is required" }}
            placeholder="password"
            name="password"
            value={password}
            onChange={(e) => {
              setPasswordError(false);
              setPassword(e.target.value);
            }}
            onBlur={(e) => {
              setPasswordError(!password);
            }}
          />
          <Form.Button content="Submit" />
        </Form.Group>
      </Form>
    </div>
  );
};
      
      







arui-feather





const BasicForm = () => {
  const [login, setLogin] = React.useState("");
  const [loginError, setLoginError] = React.useState(false);
  const [password, setPassword] = React.useState("");
  const [passwordError, setPasswordError] = React.useState(false);

  return (
    <Form
      onSubmit={(e) => {
        e.preventDefault();
        setLoginError(!login);
        setPasswordError(!password);
      }}
    >
      <FormField>
        <Input
          error={loginError && "Login is required"}
          placeholder="Login"
          value={login}
          onChange={(value) => {
            setLoginError(false);
            setLogin(value);
          }}
          onBlur={(e) => {
            setLoginError(!login);
          }}
        />
      </FormField>
      <FormField>
        <Input
          error={passwordError && "Password is required"}
          placeholder="Password"
          value={password}
          onChange={(value) => {
            setPasswordError(false);
            setPassword(value);
          }}
          onBlur={(e) => {
            setPasswordError(!password);
          }}
        />
      </FormField>
      <FormField>
        <Button view="extra" type="submit">
          Submit
        </Button>
      </FormField>
    </Form>
  );
};
      
      







yandex-ui





const BasicForm = () => {
  const [login, setLogin] = React.useState("");
  const [loginError, setLoginError] = React.useState(false);
  const [password, setPassword] = React.useState("");
  const [passwordError, setPasswordError] = React.useState(false);

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        setLoginError(!login);
        setPasswordError(!password);
      }}
      className={cnTheme(theme)}
    >
      <Textinput
        error={loginError}
        placeholder="Login"
        value={login}
        onChange={(e) => {
          setLoginError(false);
          setLogin(e.target.value);
        }}
        onBlur={(e) => {
          setLoginError(!login);
        }}
        hint={loginError && "Login is required"}
      />
      <Textinput
        error={loginError}
        placeholder="Password"
        value={password}
        onChange={(e) => {
          setPasswordError(false);
          setPassword(e.target.value);
        }}
        onBlur={(e) => {
          setPasswordError(!login);
        }}
        hint={passwordError && "Password is required"}
      />
      <Button type="submit" view="action">
        Submit
      </Button>
    </form>
  );
};
      
      







, Korus-UI , . 





Material-UI yandex-ui , Semantic-UI-React arui-feather <form>



.





, Korus-UI. .





React-

React-.









Korus-UI ()





Material-UI





Semantic-UI-React





arui-feather ( )





yandex-ui









Storybook





+

















+









+









+





+













Pull Request





70





241





2





0





0













MIT license





MIT license





MIT license





Mozilla Public License 2.0





Mozilla Public License 2.0









> 50%





+





+









+









E2E





+





















 





Chrome





85.0.4183.121





>= 49





Last 2 v.





Last 2 v.





Last 2 v.





Firefox





81.0.1





>= 52





Last 2 v.





Last 2 v.





>= 23





Edge





85.0.564.44





>=14





12+





Last 2 v.









IE





11





11





11+





11+





11+





Safari





14





>= 10





Last 2 v.





Last 2 v.









Opera













 





Last 2 v.





>= 12.1





Yandex













 





Last 2 v.





?





Android





 





 





4.4+





5+





>= 4





iOS Safari













7+





Last 2 v.





>= 5.1













+





+





+









+













+





+





+





















Typescript





Typescript





Typescript





Typescript





Typescript









GitHub , %









0,99





0,14





2





1,33













+









+





+













+





















. , .





, , .





Korus-UI

: ? React .





,   , . — , .





: , . . 





React-, . , .  , , .





Korus-UI 1,5 . opensource-. 





Korus-UI





Korus-UI : form, . , , . ,    L.form()



. , .





Korus-UI onValidationFail, , . — .





Korus-UI — . :





  • RegExp













  • ( )





  • isValid





  • unmounted





  • ,

















API





, _. css-:





<L.Div _flexBox> -> <div class="flex-box"></div>
      
      



className .





theme, css- .





Render (. ).





, :





{
  …Event, //  ,  React'
                
  //    component,     
  component: {
    isValid?: boolean, //   ,    onBlur
    name?: string, //  ,    
    value: any, //  
    … //    (. API )
  }
}
      
      



:





is: isOpen



, isValid



, isRequired



, isDisabled







has: hasCloseButton







should: shouldCorrectValue







ref



.





Korus-UI ref, React.





Korus-UI

, .   , issue   .





iOS Android. . .





React- Material-UI, Semantic-UI-React, arui-feather ( ), yandex-ui, Korus-UI (), . 





, : 





































Korus-UI, . , , Korus-UI .





. , Storybook.





  Korus-UI opensource GitHub. opensource, , :)





Je voudrais également exprimer ma gratitude à l'équipe de développement de SberCorus, le père et l'inspirateur idéologique de la bibliothèque, Artem Povolskikh . Si vous êtes intéressé par le fonctionnement du développement frontal dans SberCorus, lisez l'article d'Artyom .





Partagez votre expérience avec les bibliothèques de composants dans les commentaires. Il est intéressant de discuter des avantages et des inconvénients que vous avez rencontrés au cours de votre expérience personnelle et des fonctionnalités qui vous manquent dans le processus. 








All Articles