Vários arquivos pequenos ou um só concatenado? Vish…

Webpack 2 e o mito do HTTP/2

Vamos começar com um mito sobre o HTTP/2:

Com o HTTP/2 você não precisa unificar todos os seus arquivos em apenas um.

HTTP/2 consegue enviar múltiplos arquivos, em paralelo, através da mesma conexão. Então não há custo adicional para enviar vários arquivos. Cada arquivo pode ser cacheado separadamente.

Infelizmente, não é assim tão fácil na realidade


Trabalho inicial

Caso você seja curioso, assim como eu, recomendo ler os dois seguintes artigos (em inglês, talvez eu vá traduzi-los futuramente) que explicam detalhadamente e até fazem alguns experimentos e análises sobre esse mito:

Resumidamente, ambos concluem o seguinte:

  • Ainda há uma sobrecarga no protocolo HTTP para cada solicitação paralela em relação a um único arquivo concatenado.
  • A compressão de um único arquivo maior é melhor do que em vários pequenos.
  • Servidores são lentos em servir vários arquivos pequenos ao invés de um único arquivo maior.

Então, nós precisamos encontrar um meio termo para usar o melhor dos dois mundos. Nós podemos colocar nossos módulos em N arquivos finais, onde N é maior do que 1 e menor que o número de módulos. Mudando um módulo, irá invalidar o cache para apenas um arquivo final que é apenas uma parte da aplicação completa. Os arquivos finais restantes da aplicação permanecem cacheados.

Mais arquivos finais significam um cache melhor, mais perdemos compressão no final.

AggressiveSplittingPlugin e Webpack 2

O Webpack 2 terá todas essas possibilidades por padrão. Aliás, internamente, o Webpack já suporta isso. Nós já temos chunks como um grupo de módulos que criam um arquivo final. Nós temos uma fase de otimização que já modificam esses chunks. Nós só precisamos de um plugin para criar a separação desses chunks em múltiplos pequenos arquivos.

E é aí que entra o AggressiveSplittingPlugin, que irá dividir o chunk original em vários pequenos chunks. Você pode especificar o tamanho que esses chunks terão. Isso melhora o cache, mas piora a compressão (e também a transferência no HTTP/1).

Para combinar módulos similares, eles serão ordenados alfabeticamente (por caminho do arquivo), antes de serem divididos. Módulos que estiverem na mesma pasta provavelmente são relacionados uns com os outros. Com isso em mente, eles acabam no mesmo arquivo chunk, o mesmo serve para compressão.

Agora nós temos um modelo eficiente de separação e otimização de chunks.


Cache e otimizações do Webpack

E depois de tudo isso, infelizmente não é o final da história. Quando a aplicação for atualizada, nós precisamos tentar seriamente reusar os chunks já criados. Isso quer dizer, que cada vez que o AggressiveSplittingPlugin acha um chunk de acordo com sua configuração (do tamanho limite que você configurou), ele guarda um hash do módulo no chamado Records.

Records é o conceito que o Webpack usa para salvar o estado entre as aplicações. Ele lê e salva em um arquivo JSON.

Quando o AggressiveSplittingPlugin é chamado após sua primeira vez, primeiramente ele tenta recuperar o estado dos chunks desse modelo Records antes de dividir o restante dos módulos. Esse assegura que chunks cacheados sejam reutilizados.


Colocando tudo junto

Uma aplicação que esteja usando essa técnica não mais emite apenas um arquivo que é incluído no HTML. Ela na verdade emite vários chunks que devem ser carregados. Com essa técnica de otimização, a aplicação acaba precisando ter várias tags <script> que irão carregar os chunks (em paralelo). Talvez algo como:

Webpack emite esses arquivos em ordem de idade. O arquivo mais antigo (aquele não foi modificado a um bom tempo) é executado primeiro, e os mais recentes (que foram modificados) são executados depois. O navegador começa a executar os arquivos do cache enquanto aguarda o carregamento dos arquivos mais recentes. É mais provável que os arquivos mais antigos já estejam no cache.

A técnica HTTP/2 Server Push pode ser usada para enviar esses chunks para o navegador quando a página HTML for solicitada. É melhor começar enviando os arquivos mais recentes primeiro, já que os antigos estarão em cache. O navegador pode cancelar as solicitações de Server Push para arquivos que ele já tem, mas isso irá custar outra round trip ao servidor.

Quando você estiver usando essa técnica para carregar arquivos em demanda, Webpack irá cuidar das requisições dos arquivos em paralelo para você.


Conclusão

Webpack 2 trará todas as ferramentas para otimizar o cache e transferência de arquivos para sua aplicação quando você estiver usando HTTP/2.

O plugin AggressiveSplittingPlugin está em estado experimental.

Com todas essa possibilidades, eu estou curioso para ver o que você irá criar.


Créditos