Como eu derrubei 250KB de peso de CSS morto com PurgeCSS

Foto de Lena L no Unsplash

Sou um grande defensor do CSS do utilitário . Depois de tentar vários métodos ao longo dos anos, descobri que é a melhor, mais sustentável e escalável forma de escrever CSS até hoje .

Quando meu colega de trabalho Clément Denoix e eu criamos o api-search.io , decidi usar o Tailwind CSS para estilizá- lo. O Tailwind CSS é uma biblioteca de primeiro utilitário independente de temas, totalmente personalizável.

O ponto principal de uma biblioteca é dar a você acesso a um amplo conjunto de ferramentas para usar à vontade. O problema é que, como normalmente você usa apenas um subconjunto dele, você acaba com muitas regras CSS não usadas em sua compilação final .

No meu caso, não apenas carreguei toda a biblioteca CSS Tailwind, mas também adicionei várias variantes a alguns módulos. Isso acabou fazendo com que o tamanho final do arquivo CSS fosse 259 KB (antes do GZip). Isso é muito pesado quando você considera que o site é um aplicativo simples de uma única página com um design minimalista.

Você não quer carregar cada utilitário manualmente quando precisar. Essa seria uma tarefa longa e complicada. Um cenário melhor é ter tudo à sua disposição durante o desenvolvimento e remover automaticamente o que você não usou durante a etapa de criação .

Em JavaScript, chamamos de tremor de árvore . Agora, graças ao PurgeCSS , você pode fazer o mesmo com sua base de código CSS .

PurgeCSS analisa seus arquivos de conteúdo e seu CSS, em seguida, combina os seletores juntos. Se não encontrar nenhuma ocorrência de um seletor no conteúdo, ele será removido do arquivo CSS.

Na maior parte, isso pode funcionar fora da caixa . No entanto, existem algumas áreas em qualquer site que podem exigir um pouco mais de raciocínio antes de deixar o PurgeCSS fazer sua mágica.

Dividindo meu CSS

O projeto contém três arquivos CSS principais:

  • Uma redefinição de CSS chamada normalize.css , incluída no Tailwind CSS.
  • Tailwind CSS , a parte mais importante da minha base de código CSS.
  • Algum CSS personalizado, principalmente para estilizar os componentes do InstantSearch aos quais não pude adicionar classes.

O PurgeCSS não pode detectar que eu preciso manter seletores como .ais-Highlight , porque os componentes que o usam só aparecem no DOM em tempo de execução . O mesmo acontece com normalize.css : estou confiando nele para redefinir estilos de navegadores, mas muitos dos componentes relacionados nunca serão correspondidos porque são gerados em JavaScript.

No caso de aulas que começam com .ais- , podemos classificá-las com a lista branca . Agora, quando se trata de redefinir estilos, os seletores são um pouco mais complicados de rastrear. Além disso, o tamanho de normalize.css é bastante insignificante e não é obrigado a mudar. Então, neste caso, decidi ignorar o arquivo completamente. Consequentemente, tive que dividir os estilos antes de executar o PurgeCSS .

Minha configuração inicial de CSS ficou assim:

  • Um arquivo tailwind.src.css com três diretivas @tailwind : preflight , components e utilities .
  • Um arquivo App.css com meus estilos personalizados.
  • Um script npm em package.json para criar o Tailwind CSS antes de iniciar ou construir o projeto. Toda vez que esse script é executado, ele gera um arquivo tailwind.css em src , que é carregado no projeto.

A @tailwind preflight carrega normalize.css . Eu não queria que o PurgeCSS tocasse, então mudei para um arquivo separado.

 // tailwind.src.css @tailwind components;
 utilitários @tailwind ; / * normalize.src.css * / @windwind preflight;

Então, eu mudei meu script tailwind existente em package.json para construir o normalize.src.css separadamente.

 { 
 "scripts": { 
 "tailwind": "npm run tailwind: normalize && npm run tailwind: css", 
 "tailwind: normalize": "tailwind construir src / normalize.src.css -c tailwind.js -o src / normalize.css", 
 "tailwind: css": "tailwind construir src / tailwind.src.css -c tailwind.js -o src / tailwind.css" 
 } 
 }

Finalmente, eu carreguei normalize.css no projeto.

 // src / index.js
 ... 
 import './normalize.css' 
 import './tailwind.css' 
 importar o aplicativo de './App' 
 ...

Agora, posso executar o PurgeCSS no tailwind.css sem temer que ele possa desmontar os conjuntos de regras necessários.

Configurando o PurgeCSS

PurgeCSS vem em muitos sabores: uma interface de linha de comando, uma API JavaScript, wrappers para Webpack, Gulp, Rollup e assim por diante.

Usamos o Create React App para inicializar o site, então o Webpack veio pré-configurado e oculto por trás dos scripts- reag. Isso significa que não consegui acessar os arquivos de configuração do Webpack, a menos que eu executei a npm run eject para recuperá-los e gerenciá-los diretamente no projeto.

Não ter que gerenciar o Webpack você mesmo tem muitas vantagens, então ejetar não era uma opção. Em vez disso, decidi usar um arquivo de configuração personalizado para o PurgeCSS e um script npm.

Eu criei pela primeira vez um purgecss.config.js na raiz do projeto:

 module.exports = { 
 content: ['src / App.js'], 
 css: ['src / tailwind.css'] 
 }
  • A propriedade de content usa uma matriz de arquivos para analisar para corresponder aos seletores CSS.
  • A propriedade css usa uma matriz de folhas de estilo para eliminar.

Em seguida, editei meus scripts npm para executar o PurgeCSS:

 { 
 "scripts": { 
 "start": "npm run css && react-scripts start", 
 "build": "npm run css && react-scripts build", 
 "css": "npm run tailwind && npm run purgecss", 
 "purgecss": "purgecss -c purgecss.config.js -o src" 
 } 
 }
  • Eu adicionei um script purgecss que pega meu arquivo de configuração e purgecss a folha de estilo purgada em src .
  • Eu fiz este script ser executado toda vez que começamos ou construímos o projeto.

O CSS do Tailwind usa caracteres especiais, portanto, se você usar o PurgeCSS pronto para uso, ele poderá remover os seletores necessários. Felizmente, o PurgeCSS nos permite usar um extrator personalizado , que é uma função que lista os seletores usados ??em um arquivo. Para o Tailwind, eu precisava criar um personalizado :

 module.exports = { 
 ... 
 extratores: [ 
 { 
 extractor: class { 
 extrato estático (conteúdo) { 
 return content.match (/ [A-z0-9 -:  /] + / g) || [] 
 } 
 extensões: ['js'] 
 } 
 } 
 ] 
 }

Classes de tempo de execução da lista de permissões

O PurgeCSS não pode detectar classes que são geradas no tempo de execução , mas permite definir uma lista de desbloqueio. As classes que você whitelist permanecem no arquivo final, não importa o quê.

O projeto usa o React InstantSearch , que gera componentes com classes que começam com ais- . Convenientemente, o PurgeCSS suporta padrões na forma de expressões regulares.

 module.exports = { 
 ... 
 css: ['src / tailwind.css', 'src / App.css'], 
 whitelistPatterns: [/ais-.*/], 
 ... 
 }

Agora, se eu esquecer de remover uma classe que não uso mais do App.css , ela será removida da compilação final, mas meus seletores do InstantSearch permanecerão seguros.

Nova compilação, CSS mais leve

Com essa nova configuração, meu arquivo CSS final passou de 259 KB para… 9 KB! É bastante significativo no contexto de todo um projeto, especialmente porque muitos países ainda têm Internet lenta e instável, e mais e mais pessoas navegam em seus telefones enquanto estão em movimento.

A acessibilidade também se destina a atender pessoas com conexões de baixa largura de banda. Não é aceitável não tentar ajudar seus usuários com uma Internet mais lenta, especialmente se o que você está fazendo para fazer o download for um código morto.

Vale a pena tirar um momento para otimizar sua construção. ?