Лучше, быстрее, мощнее: styled-components v4

Автор оригинала: Evan Jacobs
  • Перевод
Автор материала, перевод которого мы публикуем сегодня, хочет представить сообществу веб-разработчиков бета-версию библиотеки styled-components v4. Он, выступая от лица создателей библиотеки, говорит, что теперь в styled-components имеется новое глобальное API для работы со стилями и нативная поддержка свойств as и ref. Библиотека пошла по пути отказа от .extend, она совместима со StrictMode React v16 и стала лучше, быстрее и мощнее.



Особенности styled-components v4


Для того чтобы установить свежую версию styled-components, воспользуйтесь такой командой:

npm install styled-components@beta

Вы можете ознакомиться с возможностями библиотеки, проверить их, и, если окажется, что что-то в ней нуждается в улучшении — сообщить об этом разработчикам. Здесь можно найти инструкцию по переходу на новую версию библиотеки. Вот журнал изменений для styled-components v4.0.0-beta.0.

Рассмотрим основные особенности этого релиза styled-components:

  • Уменьшение размера и увеличение быстродействия. Размер библиотеки уменьшен с 16.1 Кб до менее чем 15 Кб (её итоговый размер зависит от вашего бандлера и от использования плагина babel). Скорость монтирования возросла примерно на 25%, скорость повторного рендеринга — примерно на 7.5%.
  • Новое API createGlobalStyle, которое является заменой старого API injectGlobal с поддержкой горячей перезагрузки и тем.
  • Поддержка свойства as — более гибкой альтернативы .withComponent().
  • Избавление от Comp.extend с поддержкой автоматического перевода кодовой базы на унифицированный формат styled(Comp).
  • Полная совместимость со StrictMode React v16. Это, кроме того, означает, что разработчикам пришлось отказаться от поддержки React v15 и других, более старых версий React (хотя, для организации работы со styled-components v4 в React v15, вероятно, можно использовать полифиллы).
  • Нативная поддержка ref для любых стилизованных компонентов, и, благодаря React v16, отсутствие необходимости использования innerRef.

Библиотека styled-components выпущена в виде бета-версии для того, чтобы у тех, кто ей пользуется, было бы достаточно времени для стресс-тестирования изменений, и для того, чтобы можно было подготовить вспомогательные механизмы вроде описаний типов и средств подсветки синтаксиса для новых API. Ожидается, что библиотека будет пребывать в статусе бета-версии около месяца.

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


Когда была выпущена вторая версия styled-components, мы обещали, после доработки основных API, сосредоточиться на производительности. С тех пор, благодаря патчам, мы повышали производительность библиотеки, что, в частности, привело к 10-кратному росту производительности в v3.1.

Работа над быстродействием styled-components продолжается. Благодаря внутренним оптимизациям, касающимся работы с памятью, благодаря учёту особенностей реализации JS-движка и рефакторингу кода, скорость монтирования styled-components v4 и для глубоких, и для широких деревьев компонентов, возросла примерно на 25%. Обновления динамических стилей стали быстрее примерно на 7%. Вот сравнение производительности styled-components v3 и v4, проведённое по результатам трёх испытаний. Первые два связаны с исследованием монтирования деревьев компонентов, третье касается обновления компонентов с динамическими стилями.


Сравнение производительности styled-components v3 и v4

Эти результаты, полученные в изолированном окружении, выглядят впечатляюще. Однако довольно интересно будет сравнить styled-components v4 с другими библиотеками экосистемы CSS-in-JS, в частности, по скорости монтирования.


Сравнение производительности styled-components v4 и других библиотек

Как можно видеть, производительность styled-components выглядит весьма впечатляюще. В частности, в сравнении с самыми быстрыми библиотеками, результаты styled-components находятся в пределах стандартного отклонения от их результатов по показателям скорости монтирования и обновления. Всё это позволяет официально заявить о том, что производительность больше не является проблемой этой библиотеки.

Хотя производительность styled-components — это результат развития библиотеки и немалых затрат времени и сил на её совершенствование, это не означает, что улучшениями производительности мы больше заниматься не будем. Если мы обнаружим возможность сделать библиотеку ещё быстрее — мы этой возможностью воспользуемся.

Новое API глобальной стилизации


Мы уже довольно долго занимались разработкой нового API глобальной стилизации. Старое API, injectGlobal, имеет три серьёзных недостатка: оно не поддерживает динамическое обновление, горячую перезагрузку и контекстуальное использование тем.

Теперь мы с удовольствием представляем вам createGlobalStyle — новое API глобальной стилизации, поддерживающее динамическое обновление. Вот как выглядит работа с ним:

import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
  html {
    color: red;
  }
`;
export default function App() {
  return (
    <div>
      <GlobalStyle />
      This is my app!
    </div>
  );
}

Благодаря использованию createGlobalStyle глобальные стили теперь являются частью дерева компонентов React. Хотя, на первый взгляд, ничего особенного в этом нет, это даёт возможность динамического обновления и горячей перезагрузки стилей, а также позволяет использовать контекстуальные темы для глобальных стилей. Выглядит всё это точно так же, как при работе с любыми другими стилизованными компонентами.

import { createGlobalStyle, ThemeProvider } from "styled-components";
// Глобальные стили, которые теперь поддерживают обновления и темы
const GlobalStyle = createGlobalStyle`
  html {
    background: ${p => p.backgroundColor}
    color: red;
    font-family: ${p => p.theme.fontFamily};
  }
`;
export default function App() {
  return (
    <ThemeProvider theme={{ fontFamily: "Helvetica Neue" }}>
      <GlobalStyle backgroundColor="turquoise" />
    </ThemeProvider>
  );
}

Отказ от .extend и «сворачивание» компонентов


В этом релизе styled-components имеется внутренний механизм, благодаря которому стилизованные компоненты в обёртках теперь автоматически «сворачиваются», что позволяет рендерить лишь один компонент.

Что это означает для пользователей библиотеки? Дело в том, что API StyledComp.extend появилось в библиотеке для того, чтобы позволить выполнить некоторые оптимизации, основываясь на том факте, что расширяемые компоненты были стилизованными компонентами. Благодаря внутренней работе над автоматическим «сворачиванием» компонентов, при использовании styled(StyledComp) автоматически применяются те же оптимизации, которые применялись благодаря StyledComp.extend. Это означает, что от .extend теперь особенной пользы нет, что позволило отказаться от этого API. Как результат, теперь пользователи библиотеки могут писать меньше кода и получают возможность не тратить время на освоение дополнительного одного API. Мы полагаем, что это очень хорошо.

Полиморфное свойство as


Есть ещё одна новая возможность styled-components v4, к которой мы относимся с большим энтузиазмом. Речь идёт о нативной поддержке свойства as для любых стилизованных компонентов, что позволяет динамически, во время выполнения программы, влиять на рендеринг. Рассмотрим пример:

import styled from "styled-components"
import { Link } from "react-router-dom"
// <Component /> рендерит в DOM элемент div
const Component = styled.div`
  color: red;
`
<Component>Hello World!</Component>
// Но мы можем сделать так, чтобы он рендерил любой другой HTML-тег или компонент!
<Component as="span">Hello World!</Component>
<Component as={Link} to="home">Hello World!</Component>

Если сравнить это с существующим механизмом .withComponent(something), то новое средство является более гибким, так как при его использовании не нужно заранее описывать замену, и, благодаря новому внутреннему механизму «сворачивания», не теряется стилизация если базовый компонент — это styled-component!

import styled from "styled-components"
const RedColor = styled.div`
  color: red;
`
const BlueBackgroundRedColor = styled(RedColor)`
  background: blue;
`
<BlueBackgroundRedColor as="span">Hello!</BlueBackgroundRedColor>
// Даже хотя мы переключаемся на рендеринг `span` с рендеринга
// <RedColor />, объект имеет красный цвет поверх синего 
// фона!! (.withComponent на это не способен)

Как видите свойство as — это просто потрясающая штука, которая значительно упрощает рендеринг семантического HTML-кода в любом месте приложения. «Суп» из тегов <div> больше оправдывать нечем.

Обратите внимание, что пока мы не убедимся в том, что свойство as способно стать подходящей заменой для .withComponent во всех вариантах использования, мы не будем от него отказываться. Предполагается, что этот переходный период продлится не слишком долго, и мы, в очередном крупном релизе, уберём .withComponent.

React v16 и ref


В процессе перехода на API React v16 мы, кроме прочего, обнаружили, что,  благодаря новому API React.forwardRef, можно избавиться от innerRef. Нам никогда особенно не нравился этот приём, так как выглядел он как некий не особо чистый хак. Но благодаря отличной работе команды React теперь можно пользоваться нативным ref:

import styled from "styled-components"
const Component = styled.div`
  color: red;
`
// Позже в функции render
<Component ref={element => { this.myRef = element; }}

Улучшения для тех, кто пишет на TypeScript


Мы напрямую этим не занимаемся, но нам очень нравится новый @babel/preset-typescript. Его существование означает, что теперь все, кто пишет на TypeScript, наконец смогут использовать плагин babel для styled-components со всеми его полезностями, включая упрощённую отладку с применением имён компонентов в классах, поддержку серверного рендеринга и уменьшение размеров бандлов.

Кроме того, мы завершили перевод TS-типов в DefinitelyTyped. Теперь сообщество может работать с ними и исправлять ошибки типизации в собственном темпе, не привязываясь к релизам styled-components. Объявления типов можно найти здесь.

Итоги


Из этого материала вы узнали о новых возможностях бета-версии библиотеки styled-components v4 и о внесённых в неё улучшениях. Надеемся, всё это пригодится тем, кто пользуется styled-components, а, возможно, окажется интересным тем, кто только собирается эту библиотеку попробовать.

Уважаемые читатели! Пользуетесь ли вы библиотекой styled-components в своих проектах?

RUVDS.com
1 419,90
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Похожие публикации

Комментарии 21

    0
    Скажите, а styled-components умеет выносить стили в файлы?
    Просто я слышал, что процессорное время, которое значительно повышается за счет добавления минимум одной функции на каждый компонент, стоит не так дорого, как трафик, который будет значительно увеличен за счет инлайн-стилей. Такой аргумент приводя в пользу использования graphql, который за счет запросов минимизирует «ну очень дорогой трафик». Выходит что модный styled-components сводит на нет работу модного graphql? Если для facebook так дорого лишняя тысяча слов в json, то представьте сколько будет лишних инлайн-слов в тысячи компонентов состоящих из десятка компонентов? Миллионы? Получается, что если зайти по ссылке на какой-то лист, то за один только раз лишних стилей нагрузится так много, что json столько и за месяц не нагрузишь?
      0
      А ещё без них и работать наверное будет раз в сто быстрее, что сделает комфортную работу вэб-приложений на мобилках?
        0
        styled-component работает не через inline стили, а генерит css-классы которые и прокидывает в элементы
          0
          не знал. а классы он генерирует как? Для каждого компонента свой? Ведь если бы он генерировал общие классы, то это было бы тоже самое css, то есть инкапсуляции бы не было. И отсюда вопрос — стили он генерирует для каждого класса по отдельности или для множество классов один стиль?
          0
          Styled-components не умеет. Но есть более крутой вариант css-literal-loader, который как раз выносит github.com/4Catalyzer/css-literal-loader
          0
          Говорится про рост производительности, а на сколько это медленее обычного импорта стилей или css modules?
            0
            Не нужно такой треш переводить, пожалуйста, столько боли было испытано после того как попал в проект с этим чудом, это реально треш, наследование напрочь убивается, кастомизацией и гибкостью даже не пахнет, не говоря о том сколько ресурсов он жрет, нет возможности закешировать стили в продакшене, короче опыт был очень печальный, ушли на css-modules и настало счастье
              0
              если вам что-то не нужно, не значит что это не нужно другим.

              Это отличный инструмент, со своими плюсами и минусами
                +2
                Буду очень рад, если вы расскажите о своем положительном опыте работы с этим инструментом, или хотябы покажите пример как можно красиво сделать переопределение стилей при добавлении модификатора
                  0

                  Пример с модификатором


                  import { oneOf } from 'prop-types';
                  import styled from 'styled-components';
                  
                  export const Heading = styled.div`
                      font-weight: 400;
                      font-size: ${
                          ({ $size }) => ({
                              1: '32px',
                              2: '28px',
                              3: '24px',
                              4: '20px',
                              5: '16px',
                              6: '14px'
                          })[$size]
                      };  
                  `;
                  Heading.propTypes = {
                      $size: oneOf('1 2 3 4 5 6'.split(' '))
                  };
                  Heading.defaultProps = {
                      $size: '1'
                  };
                  
                  // usage
                  <Heading as={h1} $size="1">Title</Heading>

                  Минусы:


                  • раздуваются стили, исправление ожидается (можно решить временно с помощью csso минификации { restructure: true })
                  • некоторые свойства пробрасываются в html, что раздует разметку при ssr (можно решить соглашением именования пропертей, например $size или _size вместо size, или другое название проперти не из whitelist)
                    +1
                    еще один жирный минус, теперь верстальщик должен знать JS
                  0
                  В проекте для работы со стилями используется эта штука, теперь надо сделать переключение контрастной темы. Как это реализовать благодаря этой штуке непонятно.
                  0
                  да, согласен это инструмент, но этот инструмент который изобрели с целью дискриминации FRONTEND, так как очень много людей хватают все новое, не разобравшись в том, что это и пытаются на таком строить большие проекты, а больших проектах поддержка этого инструмента стоит очень много денег
                  0
                  Покажите ваш пример, где убивается наследование?
                  Что вы подразумеваете под гибкостью, конкретный пример можно?
                    0
                    function A1 { return <Div><Span>text</Span></Div>}
                    

                    теперь вызываем компонент
                    <A1 /> и в одном случае нам нужно сделать text красным,
                    а во втором сделать вокруг него падинги, и добавить бэкграунд

                    в класcическом исполнениии я просто напишу />

                    при использовании стайлета, у меня просто не будет доступа к классам которые есть у компонента и я не смогу перебить их так как они будут инлайном
                      0
                      const Div = styled.div`
                        margin-bottom: 20px;
                      `;
                      const Span = styled.span``;
                      
                      const A1 = () => (
                        <Div>
                          <Span>text</Span>
                        </Div>
                      );
                      
                      const Mode1 = styled.div`
                        ${Span} {
                          color: red;
                        }
                      `;
                      const Mode2 = styled.div`
                        ${Span} {
                          padding: 16px;
                          background: green;
                        }
                      `;
                      
                      const App = () => (
                        <div>
                          <Mode1>
                            <A1 />
                          </Mode1>
                          <Mode2>
                            <A1 />
                          </Mode2>
                        </div>
                      );

                      Edit awesome styled habr

                        0
                        скажу честно такого не видел, и не делал, мне кажется это реально жесть получается, в Вашем случае родительский компонент должен знать всегда что находится внутри дочерних компонентов, это уже больше на любителя, да с наследованием вопрос решили, но мне кажется это очень большой костыль из травмпункта.
                          0
                          А покажи свой нормальный код на обычном css, я тебе его переведу в styled и посмотрим на сколько всё плохо в styled.
                            0
                            ты меня сейчас побъешь )) но вот пример

                            import * as classNames from 'classnames';
                            import * as React from 'react';
                            import { Link } from 'react-router-dom';
                            import { Icon } from '../icon';
                            import * as styles from './logo.scss';
                            import logo from '../../svg/logo.svg';
                            import logoIcon from '../../svg/logo-icon.svg';
                            import { Responsive } from '../responsive';
                            
                            interface IProps {
                              className?: string;
                            }
                            
                            export function Logo({ className }: IProps) {
                              return (
                                <>
                                  <Responsive type={['mobile']}>
                                    <Link
                                      to='/'
                                      className={classNames(styles.logo, styles.text, className)}
                                    >
                                      <Icon svg={logo} />
                                    </Link>
                                  </Responsive>
                                  <Responsive type={['desktop', 'tablet']}>
                                    <Link to='/' className={classNames(styles.logo, styles.icon, className)}>
                                      <Icon svg={logoIcon} />
                                    </Link>
                                  </Responsive>
                                </>
                              );
                            }
                            
                            


                            @import '../../variables';
                            
                            .logo {
                              
                              &.text {
                                svg {
                                  height: 2rem;
                                  max-width: 6rem;
                                  width: inherit;
                                }
                              }
                            
                              &.icon{
                                position: relative;
                                background: $red;
                                height: 4rem;
                                line-height: 4rem;
                                width: 4rem;
                                text-align: center;
                                i {
                                  height: 2rem;
                                  width: 2rem;
                                }
                              }
                              svg {
                                 fill: $white;
                              }
                            }
                            
                            


                            ну и само сабой сетка

                            // Container widths
                            //
                            // Set the container width, and override it for fixed navbars in media queries.
                            
                            @if $enable-grid-classes {
                              .container {
                                @include make-container();
                                @include make-container-max-widths();
                              }
                            }
                            
                            // Fluid container
                            //
                            // Utilizes the mixin meant for fixed width containers, but with 100% width for
                            // fluid, full width layouts.
                            
                            @if $enable-grid-classes {
                              .container-fluid {
                                @include make-container();
                              }
                            }
                            
                            // Row
                            //
                            // Rows contain and clear the floats of your columns.
                            
                            @if $enable-grid-classes {
                              .row {
                                @include make-row();
                              }
                            
                              // Remove the negative margin from default .row, then the horizontal padding
                              // from all immediate children columns (to prevent runaway style inheritance).
                              .no-gutters {
                                margin-right: 0;
                                margin-left: 0;
                            
                                > .col,
                                > [class*="col-"] {
                                  padding-right: 0;
                                  padding-left: 0;
                                }
                              }
                            }
                            
                            // Columns
                            //
                            // Common styles for small and large grid columns
                            
                            @if $enable-grid-classes {
                              @include make-grid-columns();
                            }
                            
                            
                            
                  0

                  Действительно ли styled-components так хорош в реальных крупных проектах? Например, если сравнить с использованием CSS-препроцессора (Stylus, SASS и т.п.) отдельно от React-кода.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое