Pressione enter para ver os resultados ou esc para cancelar.

Como funciona o Styled-components por debaixo dos panos

O CSS-in-JS está se tornando cada vez mais comum no desenvolvimento moderno de front-end, especialmente na comunidade React. O styled-components se destaca na lista porque ele adota tagged templates e permite criar componentes React normais definindo apenas estilos. Ele também resolve problemas importantes, como modularidade CSS, fornece recursos non-CSS, como aninhamento, e todos esses recursos fornecidos com configuração zero. Os desenvolvedores não precisam pensar em nomes únicos para classes CSS, nem precisam pensar em classes. Mas como esse poder é alcançado?

Note: Se você não estiver familiarizado com styled-components, leia primeiro a documentação oficial.

Syntaxe Mágica

Vamos criar um botão simples com estilos estáticos usando styled-components:

Live demo

Aqui styled.button é apenas um atalho para o styled(‘button’) e uma das muitas funções criadas dinamicamente a partir da lista de elementos html disponíveis. Se você está familiarizado com tagged templates, sabe que esse button é apenas uma função e pode ser chamada com uma string simples como parâmetro do array. Vamos debugar este código:

Live demo

Agora, como você pode ver, o styled é apenas um component factory e podemos imaginar como sua implementação poderia ser.

Reinventar styled-components

Live demo

Essa implementação é muito fácil – a factory cria um novo componente baseado em uma tag name salva na closure e define inline styles após a montagem. Mas e se nosso componente tiver estilos baseado nas props?

Nós precisamos atualizar nossa implementação para avaliar as interpolations nos estilos quando um componente é montado ou as props são atualizadas.

Live Demo

A parte mais complicada é obtermos o style string:

Concatenamos todos os pedaços de string com o resultado das expressões, uma por uma, e se uma expressão é uma function, ela é chamada com as propriedades passadas no componente.

A API desta simples factory é semelhante à que o styled-components fornece, mas a implementação original é mais interessante por debaixo dos panos: ela não usa inline styles. Vamos ver mais de perto o que acontece quando você importa o styled-components e cria um componente.

styled-components por debaixo dos panos

Import styled-components

Quando você importa a biblioteca pela primeira vez em seu app, ela cria variável de contagem interna counter para contar todos os componentes criados por meio da styled factory.

Chamada styled.tag-name factory

Quando styled-components cria um novo componente, ele também cria o identificador interno componentId. Aqui está como o identificador foi computado.
O componentId para o primeiro componente em um app é sc-bdVaJa.

Atualmente styled-components usa o algoritmo MurmurHash para criar o identificador único e, em seguida, converte o número de hash para o nome alfabético.  

Assim que o identificador é criado, o styled-components insere um novo elemento 

Quando o novo componente é criado, a target component é passado para o target factory ( no nosso caso ‘button’ ) e o componente é salvo nos campos estáticos:

Como você pode ver, não sobrecarga de desempenho quando você cria um styled component. Mesmo se você definir centenas de componentes e não usá-los, tudo que você tem é um ou mais elementos

Como você pode ver o styled-components também injeta componentId (.sc-bdVaja) como uma classe CSS sem nenhuma regra.

render()

Como terminou com CSS, agora o styled-components precisa apenas criar um elemento com className correspondente:

 

Styled-components renderiza um elemento com 3 nomes de classe:

  1. this.props.className – opcional passado pelo componente pai.
  2. componentId – Identificador único de um componente, mas não de uma instância de um componente. Esta classe não possui regra CSS, mas é usada em selectors quando é necessário fazer referência a outro componente.
  1. generatedClassName – única para cada instância do componente que possui regras CSS reais.

É isso! O elemento HTML renderizado no final é:

 

componentWillReceiveProps()

 Agora vamos tentar mudar nosso button props quando ele estiver montando. Para fazer isso, precisamos criar um exemplo mais interativo para nosso button.

 Live version

Toda vez que você clicar no button, o componentWillReceiveProps() é chamado sizeValue prop incrementado e executa as mesmas ações que o componentDidMount():

  1. Avalie o tagged template.
  2. Gere o novo nome da classe CSS.
  3. Pré-processe os styles com stylis.
  4. Injetar o CSS pré-processado na página.

Se você verificar os styles gerados nas ferramentas de desenvolvimento (dev tools), depois de alguns cliques, verá:

Sim, a única diferença para cada classe CSS é a propriedade font-size e as classe CSS não usadas, não são removidas. Mas por que ? Só porque removê-los, adiciona sobrecarga de desempenho, mantendo não faz (Veja os comentários de Max’s Storiber sobre isso).

Há uma pequena otimização aqui: componentes sem interpolações na string style sao marcado como isStatic e esta flag é marcado em componentWillReceiveProps() para pular cálculos desnecessários do mesmo styles.

Dicas de desempenho

Sabendo como styled-components funcionam sob o capuz, podemos nos concentrar melhor no desempenho.

Há um ovo de páscoa no exemplo com o botão (Dica: Tente clicar no botão mais de 200 vezes e você verá a mensagem oculta do styled-components no console. Sem brincadeira 😉 ).

Se você está muito ansioso, aqui está a mensagem:

Mais de 200 classes foram geradas para o componente styled.button. Considere o uso do método attrs, junto com um objeto de estilo para styles alterado.

Exemplo:

Aqui está como o Button aparece após a refatoração:

Mas você deveria usar essa técnica para todos os seus estilos dinâmicos? Não. Mas minha regra pessoal é usar o style attr para todos os estilos dinâmicos com o número de resultados prejudicado. Por exemplo, se você tiver um componente com font-size personalizável como uma nuvem de palavras uma lista de tags carregadas de um servidor com cores diferentes, é melhor usar o style attr. Mas se você tiver vários botões como default, primary, warn e etc. Em um componente, não há problema em usar interpolação com condições na string styles.

Nos exemplos abaixo eu uso a versão de desenvolvimento, mas em produção o bundle você deve sempre usar a compilação de produção do styled-components porque é mais rápido. Assim como no React, build de produção do styled-components desabilita muitos dev warning mas, o mais importante é usar CSSStyleSheet.insertRule() para injetar styles gerados em uma página enquanto a versão de desenvolvimento usa Node.appendChild() (Aqui Evan Scott mostra como insertRule é realmente rápido).

Considere também usar babel-plugin-styled-component  Pode minify ou até processar styles antes de carregar.

Conclusão

O fluxo de trabalho do styled-component é muito simples, cria o CSS necessário antes dos componentes renderizarem e é rápido suficiente, apesar de avaliar as strings marcadas e pré-processar o CSS diretamente no browser (navegador)

Este artigo não cobre todos os aspectos do styled-components, mas tentei concentrar nos principais.

Para escrever esse post usei o styled-components v3.3.3. Desde que o artigo é sobre under-hood muitos deles podem mudar nas versões futuras.

O post original se encontra here!


***
📣
Estamos contratando pessoas que desenvolvam software!
Mais informações sobre a vaga.
***