Criando uma configuração Webpack 4 pronta para produção do zero


Webpack é um poderoso gerenciador de pacotes e dependências usado por muitas empresas de nível empresarial como ferramentas para seu código de front-end.

Normalmente, o webpack é configurado quando um projeto é configurado pela primeira vez, e pequenos ajustes são feitos nos arquivos de configuração, conforme necessário de tempos em tempos. Por esse motivo, muitos desenvolvedores não têm muita experiência trabalhando com o webpack.

Neste tutorial prático, abordaremos as noções básicas de configuração da sua própria configuração de webpack pronta para produção usando o webpack 4. Discutiremos gerenciamento de saída, gerenciamento de ativos, configurações de dev e prod, Babel, minificação, bloqueio de cache , e mais.

O Webpack agrupa seu código
O Webpack agrupa seu código

Vamos começar!


Demo App

Para os fins desta demonstração, definiremos uma configuração do webpack a partir do zero usando o webpack 4. Nosso aplicativo usará apenas JavaScript vanilla para que não fiquemos atolados em nenhum detalhe específico da estrutura. O código do aplicativo real será bem pequeno para que possamos focar mais no webpack.

Se você quiser acompanhar, todo o código deste artigo pode ser encontrado no GitHub. o ponto de partida é encontrado aqui, e as resultado final é encontrado aqui.


Ponto de partida

Para começar, começaremos com apenas alguns arquivos em nosso diretório de projetos. A estrutura de diretórios é assim:

webpack-demo
 |_ src
    |_ index.js
 |_ .gitignore
 |_ index.html
 |_ package.json
 |_ README.md
 |_ yarn.lock

o index.html arquivo é simples e agradável, apenas um cabeçalho de página e um script tag:



  
    Treinamento Webpack 1
  
  
    

Treinamento Webpack 1

o script tag referencia nossa ./src/index.js arquivo, que contém apenas algumas linhas de JavaScript que produz o texto “Hello from webpack!”:

const p = document.createElement('p')
p.textContent = 'Hello from webpack!'
document.body.append(p)

Se você arrastar o index.html arquivo no seu navegador, você poderá visualizar nossa página da Web simples:

Saída 1 do aplicativo de demonstração – olá do webpack


Instalar dependências

Eu incluí webpack e webpack-cli Como devDependencies no package.json Arquivo.

Para instalá-los, execute:

yarn install


Execução de teste do Webpack

O Webpack 4 é configurado como uma ferramenta “zero config”, o que significa que você pode executá-lo imediatamente, sem fazer nenhuma configuração inicial. Agora, para qualquer projeto real que você vai precisa fazer algumas configurações, mas é bom que você possa pelo menos fazer uma verificação rápida de sanidade para garantir que o webpack seja capaz de executar sem ter que passar por várias etapas de configuração inicial.

Então, vamos dar uma olhada. Corre:

yarn webpack

Agora você deve ver um dist diretório criado no diretório do projeto. E dentro dele você deve ver um main.js arquivo, que é o nosso código minificado.

Ótimo! O Webpack parece estar funcionando.


Consulte o código de saída

Ok, agora que temos o código JavaScript em nossa dist diretório, vamos ter o nosso index.html referência de arquivo que. Ao invés de script tag assim:

Vamos mudar para isso:

Agora, atualize a página no seu navegador e você ainda verá exatamente a mesma saída, somente desta vez o “Hello from webpack!” texto está sendo gerado pelo ./dist/main.js arquivo agora.

Saída do aplicativo de demonstração 2 – sem alterações


Criar um arquivo de configuração do Webpack

Ok, agora que instalamos o webpack e passamos por um rápido exercício de verificação de integridade, vamos criar um arquivo de configuração real do webpack. Vamos criar um arquivo chamado webpack.config.js e coloque o seguinte código dentro dele:

const path = require('path')

module.exports = {
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
}

o entry A propriedade informa ao webpack onde nosso código fonte está localizado. É o “ponto de entrada” para o nosso aplicativo.

o output A propriedade diz ao webpack como chamar o arquivo de saída e em qual diretório colocá-lo.

Simples o suficiente, certo?

Agora vamos criar um script npm em nosso package.json Arquivo:

"scripts": {
  "build": "webpack --config=webpack.config.js"
}

Agora podemos executar nosso processo de compilação com o comando yarn build. Vá em frente e execute esse comando para verificar se você configurou as coisas corretamente. Você pode até excluir seu dist diretório antes de executar o yarn build comando para verificar se o diretório está sendo gerado.


Alterar o nome do arquivo de saída

Agora, apenas por diversão, vamos mudar o nome do arquivo de saída. Para fazer isso, abriremos nossa webpack.config.js arquivo e altere o output propriedade a partir disso:

output: {
  filename: 'main.js',
  path: path.resolve(__dirname, 'dist')
}

Para isso:

output: {
  filename: 'tacos.js',
  path: path.resolve(__dirname, 'dist')
}

Agora corra yarn build novamente para gerar a saída. Você deve ver um tacos.js arquivo em seu dist diretório agora.

Mas espere! Também vemos o velho main.js arquivo em nosso dist diretório também! Não seria bom se o webpack pudesse excluir a saída desnecessária antiga toda vez que fizermos uma nova compilação?

Tem que haver um plugin para isso.


Plugins Webpack

Plugins
foto por Feelfarbig Magazine / Unsplash

O Webpack possui um rico ecossistema de módulos chamado “plugins“, que são bibliotecas que podem modificar e aprimorar o processo de compilação do webpack. Vamos explorar vários plugins úteis à medida que continuamos a melhorar nossa configuração do webpack durante o restante deste artigo.


CleanWebpackPlugin

Limpeza
foto por The Honest Company / Unsplash

Ok, voltando ao nosso problema. Seria bom se pudéssemos limpar o dist diretório antes de cada nova compilação. Existe um plugin para isso!

Nós podemos usar o CleanWebpackPlugin para nos ajudar aqui. Primeiro, precisamos instalá-lo em nosso projeto:

yarn add --dev clean-webpack-plugin

Para usá-lo, vamos simplesmente require o plugin em nossa webpack.config.js e inclua-o no diretório plugins matriz em nossa configuração:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin()
  ]
}

Agora corra yarn build novamente e você deve ver apenas um único arquivo de saída no seu dist diretório. Problema resolvido!


HTMLWebpackPlugin

HTML
foto por Florian Olivo / Unsplash

Outra coisa que é um pouco chata com a nossa configuração é que sempre que alteramos o output nome do arquivo em nosso webpack.config.js também precisamos alterar o nome do arquivo que referimos em nosso script tag em nossa index.html Arquivo. Não seria bom se o webpack pudesse gerenciar isso para nós?

Existe um plugin para isso! Nós podemos usar o HTMLWebpackPlugin para nos ajudar a gerenciar nosso arquivo HTML. Vamos instalá-lo em nosso projeto agora:

yarn add --dev html-webpack-plugin

Ok, agora vamos mudar nossa index.html arquivo dentro do nosso src diretório para que seja um irmão do index.js Arquivo.

webpack-demo
 |_ src
    |_ index.html
    |_ index.js
 |_ .gitignore
 |_ package.json
 |_ README.md
 |_ yarn.lock

Também podemos excluir o script tag em nossa index.html arquivo, já que teremos o manipulador do webpack inserindo o apropriado script tag para nós. Exclua essa linha para que seu index.html o arquivo fica assim:



  
    Webpack Training 1
  
  
    

Webpack Training 1


Agora vamos require este plugin em nossa webpack.config.js e inclua-o no diretório plugins array em nossa configuração, como fizemos no primeiro plugin:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      inject: true,
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
  ]
}

Nessas opções para o HtmlWebpackPlugin, especificamos o filename para o que gostaríamos que o arquivo de saída fosse chamado.

Nós especificamos para inject que gostaríamos que nosso arquivo JavaScript fosse injetado no body marca definindo o valor como true.

E finalmente, para o template nós fornecemos a localização do nosso index.html arquivo no src diretório.


Verificação de sanidade

Lista de controle
foto por Glenn Carstens-Peters / Unsplash

Ok, vamos ter certeza de que tudo ainda está funcionando corretamente. Corre yarn builde verifique se você vê dois arquivos no seu dist diretório: index.html e main.js.

Se você olhar atentamente em seu index.html arquivo, você verá o main.js arquivo referenciado.

Agora, abra o ./dist/index.html no navegador para verificar se a página é carregada corretamente. Se você seguiu essas etapas corretamente, sua página ainda deve estar funcionando:

Saída do aplicativo de demonstração 3 – sem alterações


Crie um servidor de desenvolvimento

Servidor
foto por Taylor Vick / Unsplash

Até agora, fizemos algumas boas melhorias usando o CleanWebpackPlugin e a HtmlWebpackPlugin. Como fizemos essas alterações, tivemos que executar manualmente o yarn build comando a cada vez para ver novas alterações em nosso aplicativo. Também acabamos de visualizar o arquivo em nosso navegador, em vez de exibir o conteúdo exibido em um servidor em execução localmente. Vamos melhorar nosso processo criando um servidor de desenvolvimento.

Para fazer isso, usaremos webpack-dev-server. Primeiro, precisamos instalá-lo:

yarn add --dev webpack-dev-server

Agora, vamos dividir nosso single webpack.config.js em dois arquivos de configuração separados, um para produção e outro para desenvolvimento. Vamos chamar o arquivo para produção webpack.config.prod.js e o arquivo para desenvolvimento webpack.config.dev.js.


Configuração do Webpack de desenvolvimento

Aqui está o nosso arquivo de configuração de desenvolvimento:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      inject: true,
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
  ]
}

Observe que especificamos o mode Como development agora, e especificamos que gostaríamos de inline-source-map para nossos arquivos JavaScript, o que significa que um mapa de origem é incluído no final de cada arquivo JavaScript. Para o nosso servidor de desenvolvimento, especificamos que nosso conteúdo será encontrado no diretório dist diretório.

Todo o restante da configuração de desenvolvimento permaneceu o mesmo.


Configuração do Webpack de produção

Agora, aqui está o nosso arquivo de configuração de produção:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      inject: true,
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
  ]
}

Este arquivo também se parece muito com o nosso arquivo de configuração original. Aqui nós especificamos que o mode é production e que gostaríamos do source-map opção para mapas de origem, que fornece arquivos de mapas de origem separados para código minificado.


Scripts NPM de produção e desenvolvimento

Por fim, vamos adicionar mais alguns scripts npm em nosso package.json para que possamos trabalhar com nossas configurações de webpack de desenvolvimento e produção:

"scripts": {
  "build": "webpack --config=webpack.config.prod.js",
  "build-dev": "webpack --config=webpack.config.dev.js",
  "start": "webpack-dev-server --config=webpack.config.dev.js --open"
}

Agora, vamos experimentar cada um desses scripts.

Corre yarn build para ver a produção construir saída. Você deve ver que o main.js arquivo em seu dist diretório é minificado e que possui um acompanhamento main.js.map arquivo de mapa de origem.

Agora corra yarn build-dev para ver o desenvolvimento criar saída. Você deve ver o main.js arquivo em seu dist diretório, mas agora observe que é não minificado.

Por fim, corra yarn start para iniciar o servidor de desenvolvimento. Isso abrirá o aplicativo em http://localhost:8080/. Não é mais necessário visualizar os arquivos diretamente, basta puxá-los para o seu navegador! Agora temos um servidor de desenvolvimento ao vivo real!

A saída que você vê ainda deve ser a mesma de sempre:

Saída 4 do aplicativo de demonstração – sem alterações


Fazendo alterações durante o desenvolvimento

Agora que temos um servidor dev funcionando, vamos experimentar fazer algumas alterações simples em nosso ./src/index.js Arquivo. Em vez de emitir “Hello from webpack!”, Vamos alterá-lo para dizer “Hello from dev server!”.

Salve o arquivo e veja a página no servidor de desenvolvimento automaticamente recarregada e atualizada para você! Isso será um bom impulso para a produtividade do desenvolvedor.

Saída 5 do aplicativo de demonstração – olá do servidor dev


Não se repita (SECO)

A única sobremesa salgada do mundo!
foto por Tobias Jelskov / Unsplash

Agora que temos dois arquivos de configuração separados do webpack, um para desenvolvimento e outro para produção, você deve ter notado que temos muito código duplicado entre os dois arquivos.

Todo desenvolvedor lá fora teve o princípio DRY em suas cabeças desde o primeiro dia: não se repita. Se você estiver escrevendo o mesmo código em vários locais, pode ser uma boa ideia transformá-lo em código compartilhado que pode ser gravado em um local e depois usado em vários locais. Dessa forma, quando você precisar fazer alterações, precisará implementá-las em um só lugar.

Então, como podemos limpar a duplicação em nossos arquivos de configuração do webpack? Existe um plugin para isso!


WebpackMerge

Mesclar
Mesclar

Nós podemos usar o webpack-merge plugin para gerenciar código compartilhado no qual vários arquivos de configuração se baseiam. Para fazer isso, primeiro instalaremos o pacote:

yarn add --dev webpack-merge

Agora, criaremos um terceiro arquivo de configuração do webpack chamado webpack.config.common.js. É aqui que manteremos nosso código compartilhado. No momento, nossos arquivos de configuração de desenvolvimento e produção compartilham o mesmo ponto de entrada, saída e plugins. Tudo o que difere entre os dois arquivos é o modo, o mapa de origem e o servidor de desenvolvimento.

Então, o conteúdo do nosso webpack.config.common.js O arquivo será:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      inject: true,
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
  ]
}

E agora, podemos mesclar esse objeto de configuração compartilhado em nossa configuração de desenvolvimento, como esta:

const merge = require('webpack-merge')
const commonConfig = require('./webpack.config.common')

module.exports = merge(commonConfig, {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
})

E podemos mesclar o objeto de configuração compartilhado em nossa configuração de produção, assim:

const merge = require('webpack-merge')
const commonConfig = require('./webpack.config.common')

module.exports = merge(commonConfig, {
  mode: 'production',
  devtool: 'source-map',
})

Veja como os dois arquivos ficam mais curtos e limpos! Bonita!


Styling Our App

Acessórios em cinza
foto por Vadim Sherbakov / Unsplash

As coisas estão parecendo muito boas com nossas configurações de webpack até agora. Temos um servidor dev de trabalho e dividimos nosso código em arquivos de desenvolvimento, produção e configuração compartilhada.

Vamos começar a trabalhar no nosso código de aplicativo atual agora. A página em preto e branco é um pouco chata de se ver. Vamos estilizar!

Na nossa src diretório, vamos criar um index.css e coloque as seguintes linhas de CSS dentro dele:

body {
  background: deeppink;
  color: white;
}

Então, no nosso ./src/index.js arquivo, vamos importar esse arquivo CSS:

import './index.css'

Agora corra yarn start para colocar nosso servidor de desenvolvimento em execução novamente.

Ah não! Temos um erro!

ERROR in ./src/index.css 1:5
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> body {
|   background: deeppink;
|   color: white;
 @ ./src/index.js 1:0-20

De que são esses “carregadores” de que fala?


Carregadores Webpack

Escadas amarelas para a doca de carregamento
foto por Kevin Butz / Unsplash

Anteriormente, discutimos plugins do webpack, que permitem estender o processo de criação do webpack. Há também um ecossistema de webpack “carregadores“, que ajuda o webpack a entender e carregar diferentes tipos de arquivos. Pronto, o webpack entende como lidar com nossos arquivos JavaScript, mas ainda não sabe o que fazer com os arquivos CSS. Vamos corrigir isso.


StyleLoader e CSSLoader

Existem dois carregadores em particular que serão úteis para nós aqui: carregador de estilo e css-loader. Vamos incluí-los em nosso projeto e depois discutir como eles funcionam.

Para começar, como sempre, precisaremos instalar essas duas dependências:

yarn add --dev style-loader css-loader

Então podemos adicioná-los ao nosso webpack.config.common.js na seção de regras do módulo, na parte inferior:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      inject: true,
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
  ],
  module: {
    rules: [
      {
        test: /.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

Esta seção define regras para o webpack para que ele saiba o que fazer com cada arquivo encontrado. o test A propriedade é uma expressão regular que o webpack verifica no nome do arquivo. Nesse caso, queremos lidar com arquivos com um .css extensão.

Então o use A propriedade informa ao webpack qual carregador ou carregadores usar para manipular arquivos que correspondem aos critérios. Observe que a ordem aqui é importante!

Os carregadores Webpack são lidos da direita para a esquerda. Então primeiro o css-loader será aplicado e, em seguida, o style-loader vai ser aplicada.


Agora, o que esses carregadores realmente fazem por nós?

css-loader interpreta e resolve arquivos CSS importados que você faz referência no seu JavaScript. Então, neste caso, css-loader ajuda a fazer esta linha funcionar:

import './index.css'

Próximo, style-loader injeta o CSS no DOM. Por padrão, style-loader pega o CSS que encontra e o adiciona ao DOM dentro de um style tag.

Vamos reiniciar nosso servidor de desenvolvimento, matando o processo atual (se você ainda o estiver executando) e iniciando-o novamente com yarn start. Agora, no navegador da web, você deve ver isso em https://localhost:8080/:

Saída do aplicativo de demonstração 6 – adiciona cores rosa e branco

Oooh, tão colorido!


Uma observação sobre outros carregadores Webpack

Não abordaremos carregadores para outros tipos de arquivo neste artigo, mas esteja ciente de que existe um carregador para tudo o que se possa imaginar! Você pode usar carregador de arquivos ou carregador de url para carregar imagens e outros ativos. Você pode usar sass-loader para gerenciar a conversão de arquivos Sass / SCSS para CSS antes de canalizar essa saída para css-loader e style-loader. O Webpack também pode lidar com menos arquivos com menos carregador se essa é a sua preferência.

A moral da história é: para qualquer tipo de arquivo, existe um carregador que pode lidar com isso.


BabelLoader

Ok, de volta ao nosso aplicativo de demonstração. Até agora, escrevemos apenas algumas linhas de JavaScript. Seria bom se pudéssemos escrever nosso JavaScript usando novos recursos que ainda não são bem suportados em todos os navegadores. Babel é um compilador JavaScript que pode transformar o código ES6 + em código ES5.

E (você adivinhou), há um carregador para isso: babel-loader.

Para configurar babel-loader, seguiremos as instruções no guia de instalação vinculado acima.

Primeiro, instalaremos nossas dependências:

yarn add --dev babel-loader @babel/core

Em seguida, adicionaremos uma nova regra à nossa matriz de regras do módulo em nossa webpack.config.common.js Arquivo:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      inject: true,
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
  ],
  module: {
    rules: [
      {
        test: /.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /.(js|jsx)$/,
        exclude: /[\/]node_modules[\/]/,
        use: {
          loader: 'babel-loader',
        },
      },
    ]
  }
}

Isso informará ao webpack que, quando encontrar .js ou .jsx arquivos para usar o Babel para transformar o código. Nós usamos o exclude para garantir que Babel não tente transformar arquivos Javascript em nosso node_modules diretório. Essas são dependências de terceiros que já deveriam ter sido cuidadas por seus criadores.


Em seguida, adicionaremos mais uma dependência para uma predefinição de Babel:

yarn add --dev @babel/preset-env

E então criaremos um .babelrc arquivo onde podemos fazer outra configuração do Babel conforme necessário. Manteremos nosso arquivo bem simples e apenas especificaremos a predefinição de Babel que queremos usar:

{
  "presets": ["@babel/preset-env"]
}


E, finalmente, vamos escrever um código ES6 em nosso ./src/index.js Arquivo:

import './index.css'

const p = document.createElement('p')
p.textContent = 'Hello from webpack!'
document.body.appendChild(p)

const p2 = document.createElement('p')
const numbers1 = [1, 2, 3, 4, 5, 6]
const numbers2 = [7, 8, 9, 10]
const numbers3 = [...numbers1, ...numbers2]
p2.textContent = numbers3.join(' ')
document.body.appendChild(p2)

Este é um exemplo realmente trivial, mas estamos usando o operador de propagação aqui para concatenar duas matrizes.


Agora, se matarmos nosso processo de execução e executarmos yarn start novamente, devemos ver isso no navegador:

Saída do aplicativo de demonstração 7 – adiciona números

Ótimo! Tudo está funcionando bem.


Estilos temporariamente ausentes

Se você desativar o cache no navegador e recarregar a página para o nosso aplicativo de demonstração, poderá observar um pequeno toque no qual a página aparece apenas com o HTML sem estilo e, em seguida, o fundo da página fica rosa e o texto fica branco como o estilos são aplicados.

Esse comportamento resulta de como style-loader trabalho. Como acima mencionado, style-loader pega CSS e o coloca em um style tag no seu HTML. Por isso, há um breve período de tempo em que o style a tag ainda não foi anexada!

Agora, isso é bom para um ambiente de desenvolvimento, mas definitivamente não queremos que esse tipo de comportamento ocorra na produção. Vamos consertar isso.


Em vez de injetar CSS em nosso HTML como style tags, podemos usar o MiniCssExtractPlugin para gerar arquivos CSS separados para nós. Usaremos isso em nossa configuração de produção enquanto ainda estiver usando style-loader na nossa configuração de desenvolvimento.

Primeiro, vamos instalar a dependência em nosso projeto:

yarn add --dev mini-css-extract-plugin

Agora em nossa webpack.config.common.js Vamos remover a regra CSS, já que lidaremos com isso de maneira diferente no desenvolvimento e produção. Ficamos com isso em nossa configuração compartilhada:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      inject: true,
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
  ],
  module: {
    rules: [
      {
        test: /.(js|jsx)$/,
        exclude: /[\/]node_modules[\/]/,
        use: {
          loader: 'babel-loader',
        },
      },
    ]
  }
}

Agora, no nosso webpack.config.dev.js arquivo, vamos adicionar novamente style-loader e css-loader que acabamos de remover de nossa configuração compartilhada:

const merge = require('webpack-merge')
const commonConfig = require('./webpack.config.common')

module.exports = merge(commonConfig, {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  module: {
    rules: [
      {
        test: /.css$/,
        use: ['style-loader', 'css-loader']
      },
    ]
  }
})

E finalmente, no nosso webpack.config.prod.js arquivo, vamos adicionar no nosso novo mini-css-extract-plugin:

const merge = require('webpack-merge')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const commonConfig = require('./webpack.config.common')

module.exports = merge(commonConfig, {
  mode: 'production',
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
  ]
})

Este é um pouco diferente, porque na verdade é um plugin e um carregador, por isso vai nas regras do módulo e nas seções de plugins.

Observe também que usamos colchetes no nome do arquivo para definir dinamicamente o name ao nome do arquivo de origem original e também inclua o contenthash, que é um hash (uma sequência alfanumérica) que representa o conteúdo do arquivo.

Agora se você correr yarn build desta vez para gerar a produção, você deve obter uma saída em seu terminal semelhante a esta:

Saída de compilação da produção Webpack
Saída de compilação da produção Webpack

Observe que atualmente ele gera um arquivo CSS e o hash do conteúdo está incluído no nome do arquivo.

Tudo bem, problema resolvido! Não há mais problema quando a página é carregada em produção, pois temos os estilos incluídos como link para um arquivo CSS real.


Rebentação de cache

Como incluímos o hash do conteúdo no arquivo CSS gerado, agora é uma boa hora para falar sobre o bloqueio de cache. Por que, você pergunta, gostaríamos que o hash de conteúdo fosse incluído em nossos nomes de arquivo? Para ajudar o navegador a entender quando um arquivo foi alterado!

Seu navegador tenta ser útil armazenando em cache os arquivos que ele já viu antes. Por exemplo, se você visitou um site e seu navegador precisou fazer o download de ativos como JavaScript, CSS ou arquivos de imagem, seu navegador pode armazenar em cache esses arquivos para que não seja necessário solicitá-los novamente no servidor.

Isso significa que, se você visitar o site novamente, seu navegador poderá usar os arquivos em cache em vez de solicitá-los novamente, para obter um tempo de carregamento da página mais rápido e uma experiência melhor.

Então, qual é o problema aqui? Imagine se tivéssemos um arquivo chamado main.js usado em nosso aplicativo. Em seguida, um usuário visita seu aplicativo e o navegador armazena em cache os main.js Arquivo. Agora, em algum momento posterior, você lançou um novo código para seu aplicativo. O conteúdo do main.js arquivo foi alterado. Porém, quando esse mesmo usuário visita seu aplicativo novamente, o navegador vê que precisa de um main.js arquivo, observa que ele possui um cache main.js e apenas usa a versão em cache. O usuário não recebe seu novo código!


Para resolver esse problema, uma prática comum é incluir o hash do conteúdo no nome de cada arquivo. Conforme discutido anteriormente, o hash do conteúdo é uma representação em cadeia do conteúdo do arquivo. Se o conteúdo do arquivo não for alterado, o hash do conteúdo não será alterado. Mas, se o conteúdo do arquivo Faz mudar, o hash do conteúdo Além disso alterar.

Como o nome do arquivo agora será alterado quando o código for alterado, o navegador fará o download do novo arquivo, pois não terá esse nome de arquivo específico em seu cache.


Incluindo o hash de conteúdo

Para incluir o hash do conteúdo em nossos nomes de arquivo JavaScript, modificaremos apenas uma linha de código em nosso webpack.config.common.js Arquivo. Está linha:

filename: 'main.js'

Mudará para esta linha:

filename: '[name].[contenthash].js'

Para que o arquivo inteiro fique assim:

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: "https://www.freecodecamp.org/./src/index.js",
  output: {
    filename: '[name].[contenthash].js', // this line is the only difference
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      inject: true,
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
  ],
  module: {
    rules: [
      {
        test: /.(js|jsx)$/,
        exclude: /[\/]node_modules[\/]/,
        use: {
          loader: 'babel-loader',
        },
      },
    ]
  }
}

Agora se você correr yarn build, você verá que seu JavaScript e seu CSS têm hashes de conteúdo incluídos:

Saída de criação de produção do Webpack com hashes de conteúdo incluídos
Saída de criação de produção do Webpack com hashes de conteúdo incluídos

Se você correr yarn build novamente e compare sua nova saída com a saída antiga, você notará que os hashes de conteúdo são exatamente os mesmos nas duas vezes.

Mas se você editar seu ./src/index.js arquivo de qualquer forma e, em seguida, execute yarn build novamente, você obterá um novo hash de conteúdo porque o conteúdo foi alterado! Tente!


Minificando CSS

Por último, mas não menos importante, podemos querer reduzir o nosso CSS. Já estamos diminuindo nosso JavaScript para a compilação de produção, mas ainda não estamos diminuindo nosso CSS. Vamos fazer isso.

Podemos minimizar nosso CSS usando o método optimize-css-assets-webpack-plugin. Vamos instalar essa dependência agora:

yarn add --dev optimize-css-assets-webpack-plugin

Agora podemos adicionar isso a uma seção de otimização do nosso webpack.config.prod.js Arquivo:

const merge = require('webpack-merge')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const commonConfig = require('./webpack.config.common')

module.exports = merge(commonConfig, {
  mode: 'production',
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
  ],
  optimization: {
    minimizer: [
      new OptimizeCssAssetsPlugin({
        cssProcessorOptions: {
          map: {
            inline: false,
            annotation: true,
          },
        },
      }),
    ],
  },
})

Agora, se corrermos yarn build e depois confira o conteúdo da nossa dist diretório, podemos ver que o CSS resultante é minificado. Agradável!

body{background:#ff1493;color:#fff}
/*# sourceMappingURL=main.66e0d6aeae6f3c6fb895.css.map */

Mas espere! Se observarmos o arquivo JavaScript resultante, ele não será minificado! Hummm. isto foi minificado antes, então o que aconteceu aqui?

O problema é que agora estamos configurando manualmente a seção minimizador de otimização da nossa configuração do webpack. Quando essa seção não está no arquivo de configuração do webpack, o webpack usa como padrão suas próprias preferências de minimizador, o que inclui a redução do JavaScript quando o mode está configurado para production.

Como agora estamos substituindo esses padrões adicionando nossas preferências para reduzir os recursos CSS, também precisamos incluir explicitamente instruções sobre como queremos que o webpack reduza os recursos JavaScript.


TerserWebpackPlugin

Podemos reduzir nossos arquivos JavaScript usando o TerserWebpackPlugin. Vamos começar instalando essa dependência:

yarn add --dev terser-webpack-plugin

Então, no nosso webpack.config.prod.js arquivo, vamos adicionar o terser-webpack-plugin às nossas configurações do minimizador de otimização na parte inferior do arquivo:

const merge = require('webpack-merge')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const commonConfig = require('./webpack.config.common')

module.exports = merge(commonConfig, {
  mode: 'production',
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
  ],
  optimization: {
    minimizer: [
      new OptimizeCssAssetsPlugin({
        cssProcessorOptions: {
          map: {
            inline: false,
            annotation: true,
          },
        },
      }),
      new TerserPlugin({
        // Use multi-process parallel running to improve the build speed
        // Default number of concurrent runs: os.cpus().length - 1
        parallel: true,
        // Enable file caching
        cache: true,
        sourceMap: true,
      }),
    ],
  },
})

Agora, se corrermos yarn build e veja a saída no dist No diretório, devemos ver que nossos arquivos CSS e JavaScript estão minificados. Aqui vamos nós!


Empacotando

Se você seguiu até aqui, recomendo!

Plantas insuficientes
foto por Katya Austin / Unsplash

Vamos revisar o que aprendemos até agora:

  • O Webpack é uma ferramenta de construção para empacotamento de ativos e gerenciamento de dependências.
  • O Webpack pode ser configurado por um arquivo de configuração.
  • Os plug-ins modificam e ampliam o processo de criação do webpack.
  • Os carregadores instruem o webpack como lidar com diferentes tipos de arquivos.
  • o clean-webpack-plugin pode ser usado para remover artefatos de construção antigos do dist diretório.
  • o html-webpack-plugin ajuda a gerenciar o arquivo HTML, incluindo a injeção de JavaScript no arquivo via script Tag.
  • webpack-dev-server cria um servidor de desenvolvimento para facilitar o desenvolvimento local.
  • É útil ter configurações separadas do webpack para desenvolvimento e produção. Você pode compartilhar e mesclar arquivos de configuração usando o webpack-merge plugar.
  • Podemos lidar com o estilo de nosso aplicativo incluindo carregadores como css-loader, style-loader, sass-loader, less-loader, e as mini-css-extract-plugin (que funciona como um plug-in e um carregador).
  • Podemos incluir nova sintaxe e recursos JavaScript usando Babel e babel-loader.
  • Podemos incluir hashes de conteúdo em nossos nomes de arquivos para ajudar a impedir o cache e gerenciar novas versões de nosso código lançado.
  • Podemos reduzir nosso CSS com o optimize-css-assets-webpack-plugin.
  • Podemos reduzir nosso JavaScript com o terser-webpack-plugin.


Qual é o próximo?

foto por Tom Parkes / Unsplash

Ao longo deste artigo, criamos uma configuração bastante respeitável do webpack. Todas essas técnicas que discutimos são padrões do setor e são comuns para uso em projetos de nível empresarial.

Mas ainda há mais! Outros tópicos avançados do webpack incluem divisão de código, carregamento lento, árvore tremendo, e mais!

Se você estiver interessado em explorar mais o webpack por conta própria, recomendo ler o site oficial guias do webpack.

Novamente, todo o código que passamos neste tutorial pode ser encontrado no GitHub. o ponto de partida é encontrado aqui, e as resultado final é encontrado aqui.

Obrigado pela leitura e feliz codificação!



Fonte

Leave a Reply

Your email address will not be published. Required fields are marked *