Webpack 4.0: Configurações Avançadas e Otimizações

Variáveis de Ambiente

Ao executar o webpack, é possível definir variáveis de ambiente através da linha de comando para personalizar a compilação. Por exemplo, para configurar um ambiente local de produção com barra de progresso:

npx webpack --env.ambiente=local --env.producao --progresso

Tree Shaking

O Tree Shaking remove código JavaScript não utiilzado do bundle final. Esta técnica funciona apenas com módulos ES6 que utilizam import e export. Para habilitá-lo, configure a opção de otimização:

otimizacao: {
  exportacoesUtilizadas: true
}

No arquivo package.json, é necessário indicar efeitos colaterais para arquivos que não devem ser analisados, como folhas de estilo:

"efeitosColaterais": ["*.css", "*.scss"]

Ambientes de Desenvolvimento e Produção

Utilize o pacote webpack-merge para criar configurações distintas para desenvolivmento e produção. O ambiente de desenvolvimento requer hot reloading, source maps detalhados e um servidor local. Em produção, busca-se bundles minificados e source maps leves.

Code Splitting (Divisão de Código)

A divisão de código pode ser realizada de duas maneiras principais. Primeiramente, definindo múltiplos pontos de entrada:

entrada: {
  principal: caminho.resolve(__dirname, '../src/index.js'),
  utilitarioLodash: caminho.resolve(__dirname, '../src/lodash.js')
}

No arquivo lodash.js, exporte a biblioteca para o escopo global:

import _ from 'lodash';
window._ = _;

Em segundo lugar, utilize a configuração optimization.splitChunks para extrair código comum de módulos síncronos:

otimizacao: {
  divisaoDeChunks: {
    blocos: 'todos'
  }
}

Para código assíncrono, basta utilizar a importação dinâmica, que será dividida automaticamente:

async function obterLodash() {
  const { default: _ } = await import(/* webpackNomeDoChunk: 'lodash' */ 'lodash');
  const elemento = document.createElement('div');
  elemento.innerHTML = _.join(['Olá', 'Mundo'], '-');
  return elemento;
}

obterLodash().then(resultado => {
  document.body.appendChild(resultado);
});

Análise de Bundle

Para analisar o conteúdo do bundle, gere um arquivo de estatísticas e utilize ferramentas como webpack-bundle-analyzer ou sites de análise:

npx webpack --perfil --json > estatisticas.json

Nomes de Arquivo e Chunks

A opção output.filename controla os nomes dos arquivos gerados a partir dos pontos de entrada. A opção output.chunkFilename é usada para chunks não listados na entrada, como aqueles carregados dinamicamente via import().

Carregamento Assíncrono de Código

Utilize importações dinâmicas para carregar código sob demanda, melhorando o desempenho inicial da página ao reduzir o tamanho do bundle principal.

Prefetching e Preloading

Para carregar recursos de forma antecipada durante o tempo ocioso da rede, use os comentários mágicos webpackPrefetch ou webpackPreload:

document.addEventListener('click', function () {
  import(/* webpackPrefetch: true */ './moduloClique').then(({ default: funcao }) => {
    funcao();
  });
});

Extração de CSS com MiniCssExtractPlugin

O plugin mini-css-extract-plugin extrai CSS para arquivos separados. A configuração básica inclui definição de nomes de arquivo:

const ExtrairCss = require('mini-css-extract-plugin');
module.exports = {
  plugins: [
    new ExtrairCss({
      nomeArquivo: '[nome].css',
      nomeArquivoChunk: '[id].css',
    }),
  ],
  modulo: {
    regras: [
      {
        teste: /\.css$/,
        uso: [
          ExtrairCss.loader,
          'css-loader',
        ],
      },
    ],
  },
};

Em desenvolvimento, habilitar o hot module replacement (HMR) para CSS:

const ExtrairCss = require('mini-css-extract-plugin');
const emDesenvolvimento = process.env.NODE_ENV !== 'production';

module.exports = {
  plugins: [
    new ExtrairCss({
      nomeArquivo: emDesenvolvimento ? '[nome].css' : '[nome].[hash].css',
      nomeArquivoChunk: emDesenvolvimento ? '[id].css' : '[id].[hash].css',
    }),
  ],
  modulo: {
    regras: [
      {
        teste: /\.(sa|sc|c)ss$/,
        uso: [
          {
            loader: ExtrairCss.loader,
            opcoes: {
              hmr: process.env.NODE_ENV === 'development',
            },
          },
          'css-loader',
          'postcss-loader',
          'sass-loader',
        ],
      },
    ],
  },
};

Em produção, minificar o CSS utilizando plugins como optimize-css-assets-webpack-plugin:

const TerserJSPlugin = require('terser-webpack-plugin');
const ExtrairCss = require('mini-css-extract-plugin');
const OtimizarCSS = require('optimize-css-assets-webpack-plugin');

module.exports = {
  otimizacao: {
    minimizador: [new TerserJSPlugin({}), new OtimizarCSS({})],
  },
  plugins: [
    new ExtrairCss({
      nomeArquivo: '[nome].css',
      nomeArquivoChunk: '[id].css',
    }),
  ],
  modulo: {
    regras: [
      {
        teste: /\.css$/,
        uso: [ExtrairCss.loader, 'css-loader'],
      },
    ],
  },
};

Cache com contenthash

Para invalidação eficiente de cache, utilize contenthash nos nomes dos arquivos. O formato é:

[<tipoHash>:contenthash:<tipoResumo>:<comprimento>]

Shimming (Adaptação de Módulos)

O plugin ProvidePlugin injeta variáveis automaticamente, eliminando a necessidade de importações explícitas em cada arquivo:

const webpack = require('webpack');
// ... dentro da configuração de plugins
new webpack.ProvidePlugin({
  _: 'lodash'
})

No código-fonte, a variável _ pode ser usada sem importação direta:

const elemento = document.createElement('div');
elemento.innerHTML = _.join(['Bem-vindo', 'Usuário'], ' ');
document.body.appendChild(elemento);

O imports-loader pode alterar o contexto de this em módulos, por exemplo, para apontar para o objeto window:

uso: 'imports-loader?this=>window'

Integração com TypeScript

Para compilar TypeScript, utilize ts-loader e configure o tsconfig.json:

modulo: {
  regras: [
    {
      teste: /\.tsx?$/,
      exclusao: /node_modules/,
      uso: [
        {
          loader: 'ts-loader'
        }
      ]
    }
  ]
}

Arquivo tsconfig.json de exemplo:

{
  "opcoesDoCompilador": {
    "diretorioSaida": "./dist",
    "modulo": "es6",
    "alvo": "es5",
    "permitirJs": true
  }
}

Código TypeScript de exemplo:

import * as _ from 'lodash';

class Saudacao {
  mensagem: string;
  constructor(msg: string) {
    this.mensagem = msg;
  }
  cumprimentar() {
    console.log(_.join([this.mensagem, 'Programador'], '_'));
  }
}

const saudacao = new Saudacao('Olá');
saudacao.cumprimentar();

Configuração do DevServer

O servidor de desenvolvimento suporta opções como historyApiFallback para rotas de aplicação de página única, proxy para redirecionar requisições e secure para conexões HTTPS.

Resolução de Módulos com resolve

Use resolve.alias para criar atalhos a diretórios frequentemente acessados:

module.exports = {
  //...
  resolver: {
    alias: {
      Utilitarios: caminho.resolve(__dirname, 'src/utilitarios/'),
      Modelos: caminho.resolve(__dirname, 'src/modelos/')
    }
  }
};

Defina extensões de arquivo preferidas com resolve.extensions:

module.exports = {
  //...
  resolver: {
    extensoes: ['.wasm', '.mjs', '.js', '.json']
  }
};

Tags: webpack webpack4 code-splitting tree-shaking TypeScript

Publicado em 6-10 04:37 por Thomas