Tecnologia
Barrel Files em aplicações JavaScript
5 minutos de leitura
Essa funcionalidade tem sido utilizada por muitos desenvolvedores em projetos de todos os níveis. Entretanto, nem todos conhecem as vantagens e desvantagens de utilizar esse recurso. Em uma JSConf, o criador do Node.js, Ryan Dahl, se arrepende de ter criado essa funcionalidade. Veremos quais são os impactos de utilizar essa funcionalidade em um projeto.
O que são barrel files?
Os arquivos de barril, em tradução direta, são arquivos para acumular exportações de módulos em um único módulo, simplificando a importação dos mesmos. Para exemplificar, vamos construir um projeto com Node.js, TypeScript e Webpack.
Para iniciar, vamos criar algumas funções em arquivos separados, denominados módulos:
// src/utils/foo.ts
export function foo() {
console.log("foo!");
}
// src/utils/bar.ts
export function bar() {
console.log("bar!");
}
// src/utils/baz.ts
export function baz() {
console.log("baz!");
}
Se quisermos importar essas funções, precisaremos fazer dessa forma:
// src/without-barrel.ts
import { foo } from "./utils/foo";
import { bar } from "./utils/bar";
import { baz } from "./utils/baz";
foo();
bar();
baz();
Agora, criando um barrel file podemos simplificar as importações:
// src/utils/index.ts
export * from "./foo";
export * from "./bar";
export * from "./baz";
// src/with-barrel.js
import { foo, bar, baz } from "utils";
foo();
bar();
baz();
Vantagens
A vantagem é centralizar as importações de um mesmo módulo, com o objetivo de diminuir linhas de importação. Imagine um cenário onde há 10 módulos importados de uma mesma pasta. Para cada módulo terá que ser escrito um import separado.
Desvantagens
Apesar de simplificar e deixar o código mais harmônico, essa prática pode trazer consequências negativas para a aplicação.
Vamos por em prática uma das desvantagens!
Para começar, vamos criar um projeto e adicionar as seguintes dependências:
npm install -D ts-loader typescript webpack webpack-cli
Precisamos configurar o TypeScript e Webpack:
// tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"module": "ES6",
"moduleResolution": "Node",
"allowJs": true,
"outDir": "./dist/",
"noImplicitAny": true
}
}
// webpack.config.js
const path = require("path");
module.exports = {
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.tsx?$/,
use: ["ts-loader"],
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
mode: "development",
};
Também vamos criar dois scripts para nos auxiliar:
// package.json
...
"scripts": {
"build:with-barrel": "webpack --entry ./src/with-barrel.ts",
"build:without-barrel": "webpack --entry ./src/without-barrel.ts",
"start": "node dist/bundle.js"
},
...
Após configurar o projeto, vamos incluir um log fora das funções nos utils/foo.ts, utils/bar.ts e utils/baz.ts:
// utils/foo.ts
export function foo() {
console.log("foo!");
}
console.log("Compilou foo");
// utils/bar.ts
export function bar() {
console.log("bar!");
}
console.log("Compilou bar");
// utils/baz.ts
export function baz() {
console.log("baz!");
}
console.log("Compilou baz");
Depois alterar os arquivos without-barrel.ts e with-barrel.ts:
// without-barrel.ts
import { foo } from "./utils/foo";
foo();
// with-barrel.ts
import { foo } from "utils";
foo();
Compilando cada arquivo, temos os seguintes outputs:
Comparando os resultados de utilizar e não utilizar barrel files, é possível notar algumas diferenças. Ao importar a função foo do barrel file e compilar a aplicação, as funções bar e baz foram incluídas no bundle. Chamamos isso de código morto.
Ao importar a função foo direto do seu módulo, não é incluído código morto. Também há redução no tamanho do bundle.
Webpack Tree shaking
Desde a versão 4 do Webpack, existe um recurso chamado Tree shaking. Consiste em remover o código morto do bundle. Podemos testar mantendo os barrel files e habilitando o tree shaking:
No package.json, devemos incluir:
// package.json
...
"sideEffects": false,
...
Compilando novamente:
Podemos observar que não compilou o código morto, obtendo assim o mesmo resultado mantendo o barrel file. Ou seja, conseguimos o melhor dos dois mundos!
Em um cenário real
Um projeto com Next.js v12 e Jest foi utilizado como cobaia. Faremos um comparativo do tempo para executar os testes unitários, tempo de build e tamanho do bundle.
Infelizmente os testes com Tree shaking do Webpack são inconclusivos com essa versão do Next.js, inclusive existe uma issue aberta para isso. Nos testes houve aumento no tempo de execução teste e build, e no tamanho do bundle. Logo foi desconsiderado.
Tempo de testes
Foi reduzido em 45% o tempo da execução dos testes com Jest:
Tempo de build
Houve uma pequena redução no tempo de build. Com barrel files passou de 6min 13seg para 5min 40seg sem barrel files.
Tamanho do bundle
Foi reduzido em 65% o tamanho do bundle comum em todas as páginas. Consequentemente também houve redução do tamanho do bundle de cada página. Na rota “/” foi reduzido 31%. Na “/pagina1” foi reduzido 28%. E por fim, na rota “/pagina2” foi reduzido 37%.
Com barrel files:
Sem barrel files:
Nova solução para Next.js: optimizePackageImports
A partir da versão 13.5 do Next.js, foi liberado uma opção chamada optimizePackageImports para solucionar o problema dos arquivos de barril. É possível configurar isso para os próprios arquivos de barril do projeto, entretanto, promete também resolver de forma automática para uma lista de bibliotecas comuns.
Conclusão
Os arquivos de barril são amados por facilitar e simplificar o código e a experiência de desenvolvimento. Como consequência em utilizar essa prática de forma desenfreada, pode trazer problemas sérios de performance, tempos de teste e build. Mas se bem configurado, é possível continuar utilizando sem problemas. Cada caso precisa ser avaliado para determinar seu uso.
Referências
- TYPESCRIPT DEEP DIVE. Barrel.
- FÂCIU, Adrian. Barrel files: to use or not to use?
- JORDÃO, Tássio. Barrel files and why you should STOP using them now.
- SCHÖNMANN, Tom. Barrel files in JavaScript.
- POZZI, Renato. Your Next.js Bundle Will Thank You.
- WEBPACK. Tree Shaking.
- DING, Shu. How we optimized package imports in Next.js.