Minifique e compacte payloads de rede com o brotli

Michael DiBlasio
Michael DiBlasio

Este codelab é uma extensão do codelab "Minimizar e compactar payloads de rede" e pressupõe que você conheça os conceitos básicos de compactação. Em comparação com outros algoritmos de compactação, como gzip, este codelab mostra como a compactação Brotli (br) pode reduzir ainda mais as taxas de compactação e o tamanho geral do app.

Captura de tela do aplicativo

Medir

Antes de adicionar otimizações, é sempre bom analisar o estado atual do aplicativo.

  1. Clique em Remixar para editar para tornar o projeto editável.
  2. Para visualizar o site, pressione Ver app e depois Tela cheia tela cheia.

No codelab anterior sobre minificação e compactação de payloads de rede, reduzimos o tamanho de main.js de 225 KB para 61,6 KB. Neste codelab, você vai aprender como a compactação Brotli pode reduzir ainda mais o tamanho do pacote.

Compactação Brotli

Brotli é um algoritmo de compactação mais recente que pode oferecer resultados de compactação de texto ainda melhores do que gzip. De acordo com a CertSimple, a performance do Brotli é:

  • 14% menor que gzip para JavaScript
  • 21% menor que gzip para HTML
  • 17% menor do que gzip para CSS

Para usar o Brotli, seu servidor precisa ser compatível com HTTPS. O Brotli é compatível com todos os navegadores modernos. Os navegadores compatíveis com Brotli incluem br nos cabeçalhos Accept-Encoding:

Accept-Encoding: gzip, deflate, br

Para determinar qual algoritmo de compactação é usado, consulte o campo Content-Encoding na guia "Rede" das Ferramentas para desenvolvedores do Chrome (Command+Option+I ou Ctrl+Alt+I):

Painel "Rede". A coluna "Codificação de conteúdo" mostra as codificações usadas para vários recursos, incluindo gzip e brotli (br).

Como ativar o Brotli

A forma como você configura um servidor da Web para enviar recursos codificados em Brotli depende de como você planeja codificá-los. Você pode compactar recursos dinamicamente com Brotli no momento da solicitação (dinâmica) ou codificá-los com antecedência para que já estejam compactados quando o usuário os solicitar (estática).

Compactação dinâmica

A compactação dinâmica envolve a compactação de recursos em tempo real à medida que são solicitados pelo navegador.

Vantagens

  • Não é necessário criar e atualizar versões compactadas salvas de recursos.
  • A compactação dinâmica funciona muito bem para páginas da Web geradas dinamicamente.

Desvantagens

  • A compactação de arquivos em níveis mais altos para alcançar melhores taxas de compactação leva mais tempo. Isso pode causar um impacto no desempenho, já que o usuário precisa esperar que os recursos sejam compactados antes de serem enviados pelo servidor.

Compactação dinâmica com Node e Express

O arquivo server.js é responsável por configurar o servidor Node que hospeda o aplicativo.

const express = require('express');
const app = express();
app.use(express.static('public'));

const listener = app.listen(process.env.PORT, function() {
  console.log(`Your app is listening on port ${listener.address().port}`);
});

Tudo isso importa express e usa o middleware express.static para carregar todos os arquivos estáticos HTML, JS e CSS no public/directory (e esses arquivos são criados pelo webpack a cada build).

Para garantir que todos os recursos sejam compactados usando brotli sempre que forem solicitados, use o módulo shrink-ray. Comece adicionando como um devDependency em package.json:

"devDependencies": {
  // ...
  "shrink-ray": "^0.1.3"
},

E importe para o arquivo do servidor, server.js:

const express = require('express');
const shrinkRay = require('shrink-ray');

e adicione como um middleware antes de express.static ser montado:

// ...
const app = express();

// Compress all requests
app.use(shrinkRay());
app.use(express.static('public'));

Agora, atualize o app e confira o tamanho do pacote no painel "Rede":

Tamanho do pacote com compactação dinâmica Brotli.

Agora você pode ver que brotli é aplicado de bz no cabeçalho Content-Encoding. main.bundle.js foi reduzido de 225 KB para 53,1 KB. Isso é cerca de 14% menor em comparação com gzip (61,6 KB).

Compactação estática

A ideia por trás da compactação estática é ter recursos compactados e salvos com antecedência.

Vantagens

  • A latência devido a altos níveis de compactação não é mais um problema. Não é necessário fazer nada na hora para compactar arquivos, já que eles podem ser buscados diretamente.

Desvantagens

  • Os recursos precisam ser compactados a cada build. Os tempos de build podem aumentar significativamente se forem usados níveis de compressão altos.

Compressão estática com Node e Express com webpack

Como a compactação estática envolve a compactação de arquivos com antecedência, as configurações do webpack podem ser modificadas para compactar recursos como parte da etapa de build. O brotli-webpack-plugin pode ser usado para isso.

Comece adicionando como um devDependency em package.json:

"devDependencies": {
  // ...
 "brotli-webpack-plugin": "^1.1.0"
},

Como qualquer outro plug-in do webpack, importe-o no arquivo de configurações, webpack.config.js:

var path = require("path");

//...
var BrotliPlugin = require('brotli-webpack-plugin');

e inclua na matriz de plug-ins:

module.exports = {
  // ...
  plugins: [
    // ...
    new BrotliPlugin({
      asset: '[file].br',
      test: /\.(js)$/
    })
  ]
},

A matriz de plug-ins usa os seguintes argumentos:

  • asset: o nome do recurso de destino.
  • [file] é substituído pelo nome original do arquivo de recurso.
  • test: todos os recursos que correspondem a essa RegExp (ou seja, recursos JavaScript que terminam em .js) são processados.

Por exemplo, main.js seria renomeado como main.js.br.

Quando o app é recarregado e recompilado, uma versão compactada do pacote principal é criada. Abra o Glitch Console para conferir o conteúdo do diretório public/ final que é veiculado pelo servidor Node.

  1. Clique no botão Ferramentas.
  2. Clique no botão Console.
  3. No console, execute os seguintes comandos para mudar para o diretório public e conferir todos os arquivos dele:
cd public
ls -lh
Tamanho do pacote com compressão estática do Brotli

A versão compactada com brotli do pacote, main.bundle.js.br, também é salva aqui e é cerca de 76% menor (225 KB contra 53 KB) do que main.bundle.js.

Em seguida, diga ao servidor para enviar esses arquivos compactados com brotli sempre que as versões originais em JS forem solicitadas. Isso pode ser feito definindo uma nova rota em server.js antes que os arquivos sejam veiculados com express.static.

const express = require('express');
const app = express();

app.get('*.js', (req, res, next) => {
  req.url = req.url + '.br';
  res.set('Content-Encoding', 'br');
  res.set('Content-Type', 'application/javascript; charset=UTF-8');
  next();
});

app.use(express.static('public'));

O app.get é usado para informar ao servidor como responder a uma solicitação GET para um endpoint específico. Uma função de callback é usada para definir como processar essa solicitação. A rota funciona assim:

  • Especificar '*.js' como o primeiro argumento significa que isso funciona para todos os endpoints disparados para buscar um arquivo JS.
  • No callback, .br é anexado ao URL da solicitação, e o cabeçalho de resposta Content-Encoding é definido como br.
  • O cabeçalho Content-Type é definido como application/javascript; charset=UTF-8 para especificar o tipo MIME.
  • Por fim, next() garante que a sequência continue para qualquer callback que possa ser o próximo.

Como alguns navegadores podem não oferecer suporte à compactação brotli, confirme se ela é compatível antes de retornar o arquivo compactado com brotli. Para isso, verifique se o cabeçalho de solicitação Accept-Encoding inclui br:

const express = require('express');
const app = express();

app.get('*.js', (req, res, next) => {
  if (req.header('Accept-Encoding').includes('br')) {
    req.url = req.url + '.br';
    console.log(req.header('Accept-Encoding'));
    res.set('Content-Encoding', 'br');
    res.set('Content-Type', 'application/javascript; charset=UTF-8');
  }

  next();
});

app.use(express.static('public'));

Depois que o app for recarregado, confira o painel "Rede" mais uma vez.

Tamanho do pacote de 53,1 KB (de 225 KB)

Pronto. Você usou a compactação Brotli para compactar ainda mais seus recursos.

Conclusão

Este codelab mostrou como o brotli pode reduzir ainda mais o tamanho geral do seu app. Quando disponível, o brotli é um algoritmo de compactação mais eficiente que o gzip.