Minifique e compacte payloads de rede com o brotli

Michael diBlasio
Michael DiBlasio

Este codelab é uma extensão do codelab Minificar 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 explica como a compactação Brotli pode reduzir ainda mais as taxas de compactação e o tamanho geral do seu app.

Captura de tela do aplicativo

Medida

Antes de começar a 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. Em seguida, pressione Tela cheia modo tela cheia.

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

Compressão Brotli

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

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

O servidor precisa ser compatível com HTTPS para usar o Brotli. O Brotli é compatível com as versões mais recentes da maioria dos navegadores. Os navegadores compatíveis com o Brotli incluirão br nos cabeçalhos Accept-Encoding:

Accept-Encoding: gzip, deflate, br

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

Painel "Network"

Como ativar o Brotli

Compactação dinâmica

A compactação dinâmica envolve a compactação dos recursos imediatamente conforme eles são solicitados pelo navegador.

Prós

  • Não é preciso criar e atualizar versões compactadas salvas de recursos.
  • A compactação dinâmica funciona especialmente para páginas da Web que são geradas dinamicamente.

Desvantagens

  • Compactar arquivos em níveis mais altos para conseguir taxas de compactação melhores leva mais tempo. Isso pode causar um impacto no desempenho, já que o usuário aguarda a compactação dos recursos antes de serem enviados pelo servidor.

Compactação dinâmica com Node/Express

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

var express = require('express');

var app = express();

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

var listener = app.listen(process.env.PORT, function() {
  console.log('Your app is listening on port ' + listener.address().port);
});

No momento, tudo o que isso faz é importar express e usar o middleware express.static para carregar todos os arquivos estáticos HTML, JS e CSS no public/directory. Esses arquivos são criados pelo webpack a cada build.

Para garantir que todos os recursos sejam compactados usando brotli sempre que forem solicitados, o módulo shrink-ray pode ser usado. Para começar, adicione-o como um devDependency em package.json:

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

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

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

E adicione-o como um middleware antes de express.static ser montado:

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

// compress all requests
app.use(shrinkRay());

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

Atualize o app e confira o tamanho do pacote no painel "Network":

Tamanho dos pacotes com compactação Brotli dinâmica

Agora, é possível notar que brotli é aplicado a partir de bz no cabeçalho Content-Encoding. O 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 é compactar os recursos e salvá-los com antecedência.

Prós

  • A latência devido aos altos níveis de compactação não é mais uma preocupação. Nada precisa acontecer na hora para compactar arquivos, já que agora eles podem ser buscados diretamente.

Desvantagens

  • Os recursos precisam ser compactados em cada build. Os tempos de compilação podem aumentar significativamente se níveis de compactação altos forem usados.

Compactação estática com Node/Express e webpack

Como a compactação estática envolve a compactação antecipada de arquivos, 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.

Para começar, adicione-o 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-o na matriz de plugins:

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

A matriz do plug-in usa os seguintes argumentos:

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

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

Quando o app é recarregado e recriado, uma versão compactada do pacote principal é criada. Abra o Console do Glitch para conferir o que está dentro do diretório public/ final disponibilizado pelo servidor do nó.

  1. Clique no botão Ferramentas.
  2. Clique no botão Console.
  3. No console, execute os seguintes comandos para acessar o diretório public e ver todos os arquivos:
cd public
ls -lh
Tamanho dos pacotes com compactação Brotli estática

A versão compactada brotli do pacote, main.bundle.js.br, agora também está salva aqui e é aproximadamente 76% menor (225 KB x 53 KB) do que main.bundle.js.

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

var express = require('express');

var 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'));

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 lidar com essa solicitação. O trajeto funciona assim:

  • Especificar '*.js' como o primeiro argumento significa que ele funciona para todos os endpoints acionados 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 ser compatíveis com a compactação brotli, confirme se eles são compatíveis antes de retornar o arquivo compactado, verificando se o cabeçalho da solicitação Accept-Encoding inclui br:

var express = require('express');

var 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'));

Quando o app for recarregado, confira o painel Network mais uma vez.

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

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

Conclusão

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