Dominando code splitting com webpack

Tempo de leitura 6 minutos

Code Splitting é um processo em build time que quebra parte do seu código em pequenos pedaços que são requisitados de forma assíncrona, apenas quando necessário. Vamos conversar sobre como Webpack pode ajudar no processo de Code Splitting para atingir a meta de um site mais performático.

Mas primeiro, como saber quando eu tenho que quebrar o meu código em pequenos pedaços (chunks)?

Os processos mais caros quando falamos sobre renderizar um Website são os processos de download e parsing do Javascript e CSS. Sabendo desse fato, se nós não precisamos de uma parte específica do código no primeiro render, esse código é um ótimo candidato para ser quebrado em uma outra parte e ser baixado sobre demanda.

O Google Chrome tem uma ótima ferramenta chamada coverage que nos mostra qual porcentagem do código baixado está ou não está sendo usado. Isso pode ser usado como uma métrica para seguir e saber o quanto do código baixado poderia ser quebrado em partes assíncronas.

Para usar essa ferramenta abra o dev tools do Chrome, cmd/ctrl + p, e digite show coverage:

imagem mostrando como acessar a janela coverage no dev tools do Chrome

Uma vez que abrimos a opção coverage, clique no botão de reload e espere até que nosso site seja completamente carregado. Os resultados vão nos motrar o quanto do código carregado por arquivo está sendo usado. É maravilhoso! 

imagem mostrando a tab coverage, onde nos podemos ver a lista de arquivos baixados e o quanto do código de cada arquivo está sendo usado.

No exemplo abaixo, 46% do código entregue não está sendo usado, essa métrica também é relativa, dependende do tamanho do arquivo e de muitas outras variáveis. Então é sempre bom entender o contexto que estamos trabalhando quando estivermos usando essas ferramentas.

Code splitting e webpack

Com Webpack nos temos duas formas de trabalhar com Code Splitting, imports estáticos e dinâmicos. As duas formas usam dynamic imports, uma feature do es2020

Imports estáticos

Para usar imports estáticos no seu código, é muito simples:

import('path/to/myModule.js')
  .then((module) => {...})

Ou ainda melhor, criando uma função que importa o módulo quando necessário. O import do arquivo é feito apenas uma vez, depois da primeira é cacheado e na próxima vez que usar a função o módulo é retornado instantaneamente, exemplo:

const getModule = () ⇒ import('path/to/myModule.js')

getModule()
  .then(module => {...})

Imports Dinamicos

Imports dinâmicos não são de verdade dinâmicos, porque eles acontecem em build time. Ao usar imports dinâmicos estamos dizendo para o Webpack: Ei Webpack, por favor, crie todos os chunks possíveis para o caminho que eu estou te passando, seja uma pasta, ou várias em build time. Dessa forma nos conseguimos usar variáveis o que torna esse processo de import "dinâmico".

Vamos considerar que nós temos uma pasta de temas, ex:

themes
  └── dark.js
  └── light.js

Como exemplo nós poderiamos importar os temas assim:

const getTheme = (theme) ⇒ import(`path/to/themes/${theme}`)

getTheme('dark')
  .then(theme => {...})

Dessa forma o Webpack vai criar todos os possíveis chunks em build time para cada tema dentro da pasta themes, essa técnica é chamada de ContextModule dentro do código do Webpack.

Comentários Mágicos

webpackChunkName

import(/* webpackChunkName: "my-chunk-name" */ 'path/to/myModule.js')

Por default o Webpack cria os nomes dos chunks seguindo uma ordem numérica, 1.js, 2.js, 3.js, o que torna o processo de reconhecer quais arquivos foram importados difícil. Usando webpackChunkName nós podemos renomear o chunk, é importante lembrar que para esse comentário mágico funcionar nós devemos estar usando output.chunkFileName: [name].whateverDoYouWantHere.js no arquivo de configuração do Webpack.

Isso é útil apenas no modo de desenvolvimento, então podemos fazer algo do tipo ao importar o arquivo:

if(process.NODE_ENV === 'development') {
  import(/* webpackChunkName: "my-chunk-name" */ 'path/to/myModule.js')
} else {
  import('path/to/myModule.js')
}

O if vai ser removido se o build estiver sendo rodado em modo de produção, e o else se estiver rodando em modo de desenvolvimento. Isso é conhecido como dead-code elimination ou em pt-br eliminação do código morto, ferramentas como Uglify.js e outras usam isso para reduzir o tamanho do arquivo final.

webpackMode

import(/* webpackMode: "lazy" */ `path/to/themes/${theme}`)

O comentário mágico webpackMode pode receber 4 tipos de valores:

Prefetch e Preload

import(/* webpackPrefetch: true */ `path/to/themes/${theme}`)

import(/* webpackLoad: true */ `path/to/themes/${theme}`)

Os dois comentários mágicos, irão criar uma tag link com rel=prefetch ou rel=preload automaticamente para você fazer o prefetching ou preloading dos seus chunks dependendo da implementação. Se você não sabe o que prefetch e preload links fazem, eu recomendo a leitura do post Preload, Prefetch And Priorities in Chrome.

Essas foram minhas anotações do curso Web performance with webpack. Espero que você tenha aprendido alguma coisa nova hoje, te vejo no próximo post! ❤️

Diel's avatar, the image contains a border that will be full when the scroll of the page is done