Restyle как новый стандарт для создания UI в React Native

от автора

В какой-то момент я хотел решить несколько проблем с UI частью в приложении на React Native:

  1. UI был слишком медленный. И здесь важно уточнить, что он был сделан с использованием styled-components.

  2. В приложении не было возможности темизации, потому что токены импортировались как модуль в каждом компоненте. И как следствие, в приложении невозможно было сделать темную тему, потому что при смене токенов оно бы просто не перерендерилось.

Спойлер. Restyle решает обе проблемы, а еще дает дополнительные классные плюшки, о которых мы обязательно поговорим дальше в статье.

Restyle – что это?

The Restyle library provides a type-enforced system for building UI components in React Native with TypeScript. It’s a library for building UI libraries, with themability as the core focus.

Getting started | Restyle

Restyle предоставляет набор инструментов, которые позволят строить UI продуктов на основе токенов. Особенно очень хорошо такой подход залетает, когда в продукте есть Дизайн Система и этот набор токенов действительно определен.

Одно из основных преимуществ библиотеки Restyle по сравнению с той же styled-system, в том, что она не использует styled-components под капотом. В основе Restyle используются абстрактные стили StyleSheet, поэтому ожидается более высокая производительность.

Немного про токены

Что мы понимаем под токенами? Это цвета, отступы, радиусы и так далее – все строгие ограничения, касающиеся стилей компонентов.

У нас будет несколько групп токенов:

Цвета
{   "error500": "rgba(224, 25, 53, 1)",   "error300": "rgba(231, 78, 99, 1)",   "error100": "rgba(255, 242, 244, 1)",   "warning500": "rgba(255, 184, 0, 1)",   "warning100": "rgba(255, 246, 221, 1)",   "success500": "rgba(46, 181, 24, 1)",   "success100": "rgba(234, 248, 232, 1)",   "g100": "rgba(255, 255, 255, 1)",   "g200": "rgba(244, 245, 248, 1)",   "g300": "rgba(208, 211, 221, 1)",   "g400": "rgba(164, 166, 178, 1)",   "g500": "rgba(24, 24, 24, 1)", }

Отступы
{   "1": 4,   "2": 8,   "3": 12,   "4": 16,   "5": 20,   "6": 24,   "7": 28,   "8": 32,   "9": 36,   "10": 40 }

Радиусы
{   "xxs": 4,   "xs": 8,   "s": 12,   "m": 18,   "l": 20,   "xl": 24,   "xxl": 32 }

Типографика
{   "headingXL": {     "fontSize": 40,     "lineHeight": 44,     "letterSpacing": -0.4   },   "headingL": {     "fontSize": 36,     "lineHeight": 40,     "letterSpacing": -0.36   },   "headingM": {     "fontSize": 28,     "lineHeight": 32,     "letterSpacing": -0.28   },   "headingS": {     "fontSize": 22,     "lineHeight": 26,     "letterSpacing": 0   },   "headingXS": {     "fontSize": 17,     "lineHeight": 22,     "letterSpacing": 0   },   "bodyL": {     "fontSize": 17,     "lineHeight": 24,     "letterSpacing": -0.34   },   "bodyM": {     "fontSize": 15,     "lineHeight": 22,     "letterSpacing": -0.3   },   "bodyS": {     "fontSize": 13,     "lineHeight": 16,     "letterSpacing": -0.26   } }

Брейкпойнты
{   "s": 0,   "m": 360 }

А дальше эти токены нужно расшарить на всё приложение, для этого библиотека Restyle предоставляет ThemeProvider:

import {ThemeProvider, createTheme} from '@shopify/restyle'; import {   colors,    spacing,    breakpoints,    textVariants,    borderRadii } from './tokens'; import {Box,Text} from './';  const theme = createTheme({   colors,   spacing,   breakpoints,   textVariants,   borderRadii, });  export const App = () => {   <ThemeProvider theme={theme}>     <Box       flex={1}       background="g200"       margin={2}     >       <Text variant="bodyM" color="g500">Привет!</Text>     </Box>   </ThemeProvider> }

Box & Text

Компоненты Box и Text являются основными строительными блоками для UI. Box создается на основе View и нужен для оберток, Text нужен для текстовых элементов соответственно. Они создаются один раз, и дальше их просто можно переиспользовать.

import {createBox, createText} from '@shopify/restyle';  export const Box = createBox();  export const Text = createText();  export const myComponent = () => {   return (     <Box       flex={1}       background="g200"       margin={2}     >       <Text variant="bodyM" color="g500">Привет!</Text>     </Box>   ) };

Text имеет свой собственный проп variant, где он как раз принимает значение токена типографики textVariant .

Стили прям в разметке

Нет необходимости выносить стили отдельно или создавать отельные файлы. Стили можно добавлять прям в компоненте, используя Restyle компоненты Box и Text.

Да, все правильно, мы будем писать стили прям в JSX. А почему это хорошо?

  • Не нужно создавать бесконечные названия блоков (Container, Wrapper и т.д)

  • Мы сразу видим всю картину целиком, за что отвечает каждый блок

  • Стилей не так уж и много обычно

  • Мы все равно часто добавляем стили в JSX, будет все однообразно

  • Styled обертки сильно ухудшают производительность

Но все эти плюсы будут работать, если вы будете декомпозировать сложные компоненты на небольшие модули. В полотне верстки это будет выглядеть плохо.

Цвета

Цвета можно пробрасывать во все цветовые пропсы, используя ключи из темы (например g200, g300, g400).

Цветовые пропсы
  • backgroundColor [bg]

  • color

  • shadowColor

  • textShadowColor

  • borderColor

В квадратных скобках сокращенный вариант пропса

Посмотрим на пример, как это может использоваться:

<Box   backgroundColor="g300"   borderColor="g400" >   <Text     variant="bodyS"     color="g200"   >       Any text   </Text> </Box>

Отступы

Все отступы тоже можно пробрасывать прям в компоненты, используя специальные пропсы для этого. В нашем случае они добавляют отступы, кратные 4px.

Пропсы для отступов

margin [m], marginTop [mt], marginRight [mr], marginBottom [mb], marginLeft [ml], marginStart [ms], marginEnd[me], marginHorizontal [mx], marginVertical [my], padding [p], paddingTop [pt], paddingRight [pr], paddingBottom [pb], paddingLeft [pl], paddingStart [ps], paddingEnd [pe], paddingHorizontal [px], paddingVertical [py], gap [g], rowGap [rG], columnGap [cG]

В квадратных скобках сокращенный вариант пропса

// будет добавлен marginTop=12px и padding=8px <Box   marginTop={3}   padding={2} />

BorderRadius

Радиус тоже берется из темы и задается следующим образом:

<Box   borderColor="g400"   borderStyle="solid"   borderWidth={1}   borderRadius="xs" />

Кастомные стили

Через свойство style можно пробросить и обычные стили. Таким образом можно добавить разные исключения, например, если нужен отступ некратный 4 или цвет не из ДС.

// добавится отступ 5px и кастомный цвет <Box style={{ padding: 5, backgroundColor: '#ccc' }} />

Размеры девайсов

В моей ДС определены два размера девайсов, что позволяет указывать разные стили следующим образом:

// s: от 0px до 360px // m: больше 360px  // для s девайсов будет 4px отступ, а для m девайсов 8px <Box marginTop={{ s: 1, m: 2 }} />

При помощи такой конструкции можно пробрасывать любые свойства, не только отступы.

Если написать просто marginTop={1}, то стили применятся для всех устройств (s и m).

Если нам нужно написать какую-то логику в компоненте, опираясь на размеры девайсов, то можно использовать хук useResponsiveProp():

import {useResponsiveProp} from '@shopify/restyle';  export const MyComponent = () => {   const value = useResponsiveProp({     s: 'Сейчас S устройство',     m: 'Сейчса M устройство'   });      return <Text>{value}</Text>; };

 Использование токенов из темы

Нам не нужно импортировать нужные токены из темы, можно нужно просто достать их используя хук useTheme().

import {useTheme} from '@shopify/restyle';  export const MyComponent = () => {   const {colors: {g300}} = useTheme();   return <OutsideComponent color={g300}>Привет!</OutsideComponent> };

Это может пригодиться, если нам нужно пробросить именно значение цвета в сторонний компонент.

Производительность

Я произвел исследование по производительности 3 вариантов и получил интересные результаты!

Как именно измерял

Для теста был создан следующий компонент:

  • Компонент содержал 100 строк

  • В каждой строке был номер, созданный шрифтом из ДС

  • В каждой строке было 20 блоков, с радиусом из ДС и рандомным цветом из ДС

Проводилось 10 замеров рендера для каждого варианта, затем для результата бралось среднее значение.

Измерялось время рендера следующим образом:

export const Component = () => {   const start = new Date().getTime();   useEffect(() => {     const renderTime = new Date().getTime() - start;     console.log(renderTime);   }, []);    return (     ...   ); };

Вариант UI

Время рендера (милисекунды)

StyleSheet

408

Restyle

499

Styled-components

632

Оказалось, что Restyle значительно быстрее Styled-components, но естественно немного медленнее вообще нативного решения, потому что содержит дополнительные обертки и парсер атрибутов в стили.

Итог

Что мы получаем от Restyle:

  • Удобные “строительные блоки” Box и Text

  • Удобная работа с токенами ДС через атрибуты

  • Все токены достаются через хуки из Restyle контекста

  • Удобочитаемые композиции верстки сразу со стилями

  • Понятная и однозначная концепция для построения UI 

Эта библиотека точно заслуживает внимания. Не вижу причин не брать ее на вооружение прям сейчас! Спасибо за внимание ?


ссылка на оригинал статьи https://habr.com/ru/articles/763982/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *