Статьи

Руководство для начинающих по Webpack 4 и комплектации модулей

Эта статья включена в нашу книгу « Современные инструменты и навыки JavaScript» . Ознакомьтесь с основными инструментами, которые поддерживают разработку современного JavaScript.

Документы Webpack 4 утверждают, что:

Webpack — это модуль-упаковщик. Его основная цель — связывать файлы JavaScript для использования в браузере, но он также способен преобразовывать, связывать или упаковывать практически любой ресурс или ресурс.

Webpack стал одним из самых важных инструментов для современной веб-разработки. Это в первую очередь пакет модулей для вашего JavaScript, но его можно научить преобразовывать все ваши внешние ресурсы, такие как HTML, CSS, даже изображения. Он может дать вам больше контроля над количеством HTTP-запросов, которые делает ваше приложение, и позволяет вам использовать другие варианты этих ресурсов (например, Pug, Sass и ES8). Webpack также позволяет легко использовать пакеты из npm.

Эта статья предназначена для новичков в Webpack и будет охватывать начальную настройку и настройку, модули, загрузчики, плагины, разбиение кода и горячую замену модулей. Если вы найдете видеоуроки полезными, я настоятельно рекомендую Webpack от Glen Maddern от First Principles в качестве отправной точки, чтобы понять, что делает Webpack особенным. Сейчас оно немного старое, но принципы все те же, и это отличное введение.

Чтобы следовать по дому, у вас должен быть установлен Node.js. Вы также можете скачать демо-приложение из нашего репозитория GitHub .

Машина webpack 4, которая сжимает все веб-элементы

Настроить

Давайте инициализируем новый проект с помощью npm и установим webpack и webpack-cli :

 mkdir webpack-demo && cd webpack-demo npm init -y npm install --save-dev webpack webpack-cli 

Далее мы создадим следующую структуру каталогов и содержимое:

  webpack-demo |- package.json + |- webpack.config.js + |- /src + |- index.js + |- /dist + |- index.html 

расстояние / index.html

 <!doctype html> <html> <head> <title>Hello Webpack</title> </head> <body> <script src="bundle.js"></script> </body> </html> 

SRC / index.js

 const root = document.createElement("div") root.innerHTML = `<p>Hello Webpack.</p>` document.body.appendChild(root) 

webpack.config.js

 const path = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } } 

Это говорит Webpack скомпилировать код в нашей точке входа src/index.js и вывести пакет в /dist/bundle.js . Давайте добавим скрипт npm для запуска Webpack.

package.json

  { ... "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "develop": "webpack --mode development --watch", + "build": "webpack --mode production" }, ... } 

Используя команду npm run develop , мы можем создать наш первый пакет!

 Asset Size Chunks Chunk Names bundle.js 2.92 KiB main [emitted] main 

Теперь вы должны иметь возможность загрузить dist/index.html в ваш браузер и получить привет «Hello Webpack».

Откройте dist/bundle.js чтобы увидеть, что сделал Webpack. Вверху находится код начальной загрузки модуля Webpack, а справа внизу — наш модуль. Возможно, вы еще не впечатлены цветом, но если вы зашли так далеко, вы можете начать использовать ES Modules, и Webpack сможет создать пакет для производства, который будет работать во всех браузерах.

Перезапустите сборку с помощью Ctrl + C и запустите npm run build чтобы скомпилировать наш пакет в рабочем режиме .

 Asset Size Chunks Chunk Names bundle.js 647 bytes main [emitted] main 

Обратите внимание, что размер пакета уменьшился с 2,92 КБ до 647 байт .

Посмотрите еще раз на dist/bundle.js и вы увидите dist/bundle.js код. Наш пакет был уменьшен с помощью UglifyJS: код будет работать точно так же, но это будет сделано с наименьшим возможным размером файла.

  • --mode development оптимизирует скорость сборки и отладки
  • --mode production оптимизирует скорость выполнения во время выполнения и размер выходного файла.

Модули

Используя модули ES, вы можете разделить свои большие программы на множество небольших, автономных программ.

Из упаковки Webpack знает, как использовать модули ES с помощью операторов import и export . В качестве примера, давайте попробуем это сейчас, установив lodash-es и добавив второй модуль:

 npm install --save-dev lodash-es 

SRC / index.js

 import { groupBy } from "lodash-es" import people from "./people" const managerGroups = groupBy(people, "manager") const root = document.createElement("div") root.innerHTML = `<pre>${JSON.stringify(managerGroups, null, 2)}</pre>` document.body.appendChild(root) 

SRC / people.js

 const people = [ { manager: "Jen", name: "Bob" }, { manager: "Jen", name: "Sue" }, { manager: "Bob", name: "Shirley" } ] export default people 

Запустите npm run develop чтобы запустить Webpack и обновить index.html . Вы должны увидеть на экране множество людей, сгруппированных по менеджерам.

Примечание. Импортами без относительного пути, такими как 'es-lodash' являются модули из npm, установленные в /node_modules . Ваши собственные модули всегда будут нуждаться в относительном пути, таком как './people' , поскольку именно так вы можете отличить их друг от друга.

Заметьте в консоли, что размер нашего пакета увеличился до 1,41 МиБ ! На это стоит обратить внимание, хотя в этом случае нет причин для беспокойства. Используя npm run build для компиляции в производственном режиме, все неиспользуемые модули lodash из lodash-es удаляются из пакета. Этот процесс удаления неиспользованного импорта известен как встряхивание дерева , и это то, что вы получаете бесплатно с Webpack.

 > npm run develop Asset Size Chunks Chunk Names bundle.js 1.41 MiB main [emitted] [big] main 
 > npm run build Asset Size Chunks Chunk Names bundle.js 16.7 KiB 0 [emitted] main 

Погрузчики

Загрузчики позволяют запускать препроцессоры для файлов по мере их импорта. Это позволяет связывать статические ресурсы помимо JavaScript , но давайте посмотрим, что можно сделать при первой загрузке модулей .js .

Давайте сделаем наш код современным, запустив все файлы .js помощью транспилятора JavaScript следующего поколения Babel :

 npm install --save-dev "babel-loader@^8.0.0-beta" @babel/core @babel/preset-env 

webpack.config.js

  const path = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /(node_modules|bower_components)/, + use: { + loader: 'babel-loader', + } + } + ] + } } 

.babelrc

 { "presets": [ ["@babel/env", { "modules": false }] ], "plugins": ["syntax-dynamic-import"] } 

Эта конфигурация не позволяет Babel переносить операторы import и export в ES5 и включает динамический импорт — что мы рассмотрим позже в разделе о разделении кода.

Теперь мы можем использовать современные языковые функции, и они будут скомпилированы в ES5, которая работает во всех браузерах.

пререкаться

Загрузчики могут быть объединены в последовательность преобразований. Хороший способ продемонстрировать, как это работает, — импортировать Sass из нашего JavaScript:

 npm install --save-dev style-loader css-loader sass-loader node-sass 

webpack.config.js

  module.exports = { ... module: { rules: [ ... + { + test: /\.scss$/, + use: [{ + loader: 'style-loader' + }, { + loader: 'css-loader' + }, { + loader: 'sass-loader' + }] + } ] } } 

Эти загрузчики обрабатываются в обратном порядке:

  • sass-loader превращает Sass в CSS.
  • css-loader анализирует CSS в JavaScript и разрешает любые зависимости.
  • style-loader выводит наш CSS в <style> в документе.

Вы можете думать о них как о вызовах функций. Выход одного загрузчика подается как вход в следующий:

 styleLoader(cssLoader(sassLoader("source"))) 

Давайте добавим исходный файл Sass и импортируем модуль.

SRC / style.scss

 $bluegrey: #2b3a42; pre { padding: 8px 16px; background: $bluegrey; color: #e1e6e9; font-family: Menlo, Courier, monospace; font-size: 13px; line-height: 1.5; text-shadow: 0 1px 0 rgba(23, 31, 35, 0.5); border-radius: 3px; } 

SRC / index.js

  import { groupBy } from 'lodash-es' import people from './people' + import './style.scss' ... 

Перезапустите сборку с помощью Ctrl + C и npm run develop . Обновите index.html в браузере, и вы должны увидеть некоторые стили.

CSS в JS

Мы просто импортировали файл Sass из нашего JavaScript, как модуль.

Откройте dist/bundle.js и найдите «pre {». Действительно, наш Sass был скомпилирован в строку CSS и сохранен как модуль в нашем комплекте. Когда мы импортируем этот модуль в наш JavaScript, style-loader выводит эту строку во встроенный <style> .

Зачем ты это делаешь?

Я не буду углубляться в эту тему здесь, но вот несколько причин для рассмотрения:

  • Компонент JavaScript, который вы, возможно, захотите включить в свой проект, может зависеть от правильного функционирования других ресурсов (HTML, CSS, Images, SVG). Если все они могут быть объединены вместе, гораздо проще импортировать и использовать.
  • Устранение мертвого кода: если компонент JS больше не импортируется вашим кодом, CSS больше не будет импортироваться. Созданный пакет будет содержать только код, который что-то делает.
  • Модули CSS: глобальное пространство имен CSS делает очень трудно быть уверенным, что изменение вашего CSS не будет иметь никаких побочных эффектов. Модули CSS изменяют это, делая CSS локальным по умолчанию и предоставляя уникальные имена классов, на которые вы можете ссылаться в своем JavaScript.
  • Сокращайте количество HTTP-запросов, объединяя / разбивая код хитрыми способами.

Картинки

Последний пример загрузчиков, которые мы рассмотрим, — это обработка изображений с помощью file-loader .

В стандартном HTML-документе изображения выбираются, когда браузер обнаруживает тег img или элемент со свойством background-image . С Webpack вы можете оптимизировать это в случае небольших изображений, сохраняя источник изображений в виде строк внутри вашего JavaScript. Делая это, вы предварительно загружаете их, и браузеру не придется извлекать их с отдельными запросами позже:

 npm install --save-dev file-loader 

webpack.config.js

  module.exports = { ... module: { rules: [ ... + { + test: /\.(png|svg|jpg|gif)$/, + use: [ + { + loader: 'file-loader' + } + ] + } ] } } 

Загрузите тестовое изображение с этой командой:

 curl https://raw.githubusercontent.com/sitepoint-editors/webpack-demo/master/src/code.png --output src/code.png 

Перезапустите сборку с помощью Ctrl + C и npm run develop и теперь вы сможете импортировать изображения в виде модулей!

SRC / index.js

  import { groupBy } from 'lodash-es' import people from './people' import './style.scss' + import './image-example' ... 

SRC / изображение-example.js

 import codeURL from "./code.png" const img = document.createElement("img") img.src = codeURL img.style = "background: #2B3A42; padding: 20px" img.width = 32 document.body.appendChild(img) 

Это будет включать изображение, где атрибут src содержит URI данных самого изображения:

 <img src="data:image/png;base64,iVBO..." style="background: #2B3A42; padding: 20px" width="32"> 

Фоновые изображения в нашем CSS также обрабатываются file-loader .

SRC / style.scss

  $bluegrey: #2b3a42; pre { padding: 8px 16px; - background: $bluegrey; + background: $bluegrey url("code.png") no-repeat center center / 32px 32px; color: #e1e6e9; font-family: Menlo, Courier, monospace; font-size: 13px; line-height: 1.5; text-shadow: 0 1px 0 rgba(23, 31, 35, 0.5); border-radius: 3px; } 

Смотрите другие примеры загрузчиков в документах:

График зависимостей

Теперь вы сможете увидеть, как загрузчики помогают построить дерево зависимостей между вашими ресурсами. Это то, что демонстрирует изображение на домашней странице Webpack.

JS требует SCSS требует CSS требует PNG

Хотя JavaScript является отправной точкой, Webpack ценит то, что другие ваши типы ресурсов, такие как HTML, CSS и SVG, имеют свои собственные зависимости, которые следует рассматривать как часть процесса сборки.

Разделение кода

Из документов Webpack :

Разделение кода является одной из наиболее привлекательных особенностей Webpack. Эта функция позволяет вам разбить ваш код на различные пакеты, которые затем могут быть загружены по требованию или параллельно. Его можно использовать для достижения меньших пакетов и управления приоритетами загрузки ресурсов, которые при правильном использовании могут оказать значительное влияние на время загрузки.

До сих пор мы видели только одну точку входа — src/index.js — и один выходной пакет — dist/bundle.js . Когда ваше приложение будет расти, вам нужно будет разделить его так, чтобы вся кодовая база не загружалась с самого начала. Хорошим подходом является использование разделения кода и отложенной загрузки для извлечения данных по требованию, как того требуют пути кода.

Давайте продемонстрируем это, добавив модуль «чат», который выбирается и инициализируется, когда кто-то взаимодействует с ним. Мы создадим новую точку входа и дадим ей имя, а также сделаем имя выходного файла динамическим, чтобы оно отличалось для каждого чанка.

webpack.config.js

  const path = require('path') module.exports = { - entry: './src/index.js', + entry: { + app: './src/app.js' + }, output: { - filename: 'bundle.js', + filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, ... } 

SRC / app.js

 import './app.scss' const button = document.createElement("button") button.textContent = 'Open chat' document.body.appendChild(button) button.onclick = () => { import(/* webpackChunkName: "chat" */ "./chat").then(chat => { chat.init() }) } 

SRC / chat.js

 import people from "./people" export function init() { const root = document.createElement("div") root.innerHTML = `<p>There are ${people.length} people in the room.</p>` document.body.appendChild(root) } 

SRC / app.scss

 button { padding: 10px; background: #24b47e; border: 1px solid rgba(#000, .1); border-width: 1px 1px 3px; border-radius: 3px; font: inherit; color: #fff; cursor: pointer; text-shadow: 0 1px 0 rgba(#000, .3), 0 1px 1px rgba(#000, .2); } 

Примечание. Несмотря на комментарий /* webpackChunkName */ для присвоения имени /* webpackChunkName */ , этот синтаксис не является специфичным для Webpack. Это предлагаемый синтаксис для динамического импорта, предназначенный для поддержки непосредственно в браузере.

Давайте запустим npm run build и посмотрим, что это генерирует:

 Asset Size Chunks Chunk Names chat.bundle.js 377 bytes 0 [emitted] chat app.bundle.js 7.65 KiB 1 [emitted] app 

Поскольку наш входной комплект изменился, нам также потребуется обновить наш путь к нему.

расстояние / index.html

  <!doctype html> <html> <head> <title>Hello Webpack</title> </head> <body> - <script src="bundle.js"></script> + <script src="app.bundle.js"></script> </body> </html> 

Давайте запустим сервер из каталога dist, чтобы увидеть это в действии:

 cd dist npx serve 

Откройте http: // localhost: 5000 в браузере и посмотрите, что произойдет. bundle.js выбирается только bundle.js . Когда кнопка нажата, модуль чата импортируется и инициализируется.

Сетевые запросы, показывающие ленивую загрузку кусков

Без особых усилий мы добавили динамическое разделение кода и ленивую загрузку модулей в наше приложение. Это отличная отправная точка для создания высокоэффективного веб-приложения.

Плагины

В то время как загрузчики работают с преобразованиями для отдельных файлов, плагины работают с большими кусками кода.

Теперь, когда мы связываем наш код, внешние модули и статические активы, наш пакет будет быстро расти. Плагины здесь, чтобы помочь нам разумно разделить наш код и оптимизировать его для производства.

Не зная об этом, мы уже использовали многие стандартные плагины для Webpack с «mode»

развитие

  • Предоставляет process.env.NODE_ENV значение «development»
  • NamedModulesPlugin

производство

  • Обеспечивает process.env.NODE_ENV значением «production»
  • UglifyJsPlugin
  • ModuleConcatenationPlugin
  • NoEmitOnErrorsPlugin

производство

Прежде чем добавлять дополнительные плагины, мы сначала разделим нашу конфигурацию, чтобы мы могли применять плагины, специфичные для каждой среды.

Переименуйте webpack.config.js в webpack.common.js и добавьте файл конфигурации для разработки и производства.

 - |- webpack.config.js + |- webpack.common.js + |- webpack.dev.js + |- webpack.prod.js 

Мы будем использовать webpack-merge чтобы объединить нашу общую конфигурацию с конфигурацией для конкретной среды:

 npm install --save-dev webpack-merge 

webpack.dev.js

 const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'development' }) 

webpack.prod.js

 const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'production' }) 

package.json

  "scripts": { - "develop": "webpack --watch --mode development", - "build": "webpack --mode production" + "develop": "webpack --watch --config webpack.dev.js", + "build": "webpack --config webpack.prod.js" }, 

Теперь мы можем добавить плагины, специфичные для разработки, в webpack.dev.js и плагины, специфичные для продукции, в webpack.prod.js .

Сплит CSS

Рекомендуется отделять ваш CSS от JavaScript при объединении для производства с использованием ExtractTextWebpackPlugin .

.scss загрузчики .scss идеально подходят для разработки, поэтому мы переместим их из webpack.common.js в webpack.dev.js и добавим webpack.prod.js только в webpack.prod.js .

 npm install --save-dev [email protected] 

webpack.common.js

  ... module.exports = { ... module: { rules: [ ... - { - test: /\.scss$/, - use: [ - { - loader: 'style-loader' - }, { - loader: 'css-loader' - }, { - loader: 'sass-loader' - } - ] - }, ... ] } } 

webpack.dev.js

  const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'development', + module: { + rules: [ + { + test: /\.scss$/, + use: [ + { + loader: 'style-loader' + }, { + loader: 'css-loader' + }, { + loader: 'sass-loader' + } + ] + } + ] + } }) 

webpack.prod.js

  const merge = require('webpack-merge') + const ExtractTextPlugin = require('extract-text-webpack-plugin') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'production', + module: { + rules: [ + { + test: /\.scss$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: ['css-loader', 'sass-loader'] + }) + } + ] + }, + plugins: [ + new ExtractTextPlugin('style.css') + ] }) 

Давайте сравним вывод наших двух скриптов сборки:

 > npm run develop Asset Size Chunks Chunk Names app.bundle.js 28.5 KiB app [emitted] app chat.bundle.js 1.4 KiB chat [emitted] chat 
 > npm run build Asset Size Chunks Chunk Names chat.bundle.js 375 bytes 0 [emitted] chat app.bundle.js 1.82 KiB 1 [emitted] app style.css 424 bytes 1 [emitted] app 

Теперь, когда наш CSS извлечен из нашего комплекта JavaScript для производства, нам нужно <link> на него из нашего HTML.

расстояние / index.html

  <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Code Splitting</title> + <link href="style.css" rel="stylesheet"> </head> <body> <script type="text/javascript" src="app.bundle.js"></script> </body> </html> 

Это позволяет параллельно загружать CSS и JavaScript в браузере, поэтому загрузка будет быстрее, чем в одном пакете. Это также позволяет отображать стили до завершения загрузки JavaScript.

Генерация HTML

Всякий раз, когда наши выходные данные менялись, нам приходилось обновлять index.html для ссылки на новые пути к файлам. Это именно то, что html-webpack-plugin был создан, чтобы делать для нас автоматически.

Мы можем также добавить clean-webpack-plugin одновременно, чтобы очистить наш каталог /dist перед каждой сборкой.

 npm install --save-dev html-webpack-plugin clean-webpack-plugin 

webpack.common.js

  const path = require('path') + const CleanWebpackPlugin = require('clean-webpack-plugin'); + const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { ... + plugins: [ + new CleanWebpackPlugin(['dist']), + new HtmlWebpackPlugin({ + title: 'My killer app' + }) + ] } 

Теперь каждый раз, когда мы строим, dist будет очищен. Теперь мы также увидим вывод index.html с правильными путями к нашим входным пакетам.

Запуск npm run develop производит это:

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>My killer app</title> </head> <body> <script type="text/javascript" src="app.bundle.js"></script> </body> </html> 

И npm run build производит это:

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>My killer app</title> <link href="style.css" rel="stylesheet"> </head> <body> <script type="text/javascript" src="app.bundle.js"></script> </body> </html> 

развитие

Webpack-dev-server предоставляет вам простой веб-сервер и дает вам возможность перезагрузки в реальном времени , поэтому вам не нужно вручную обновлять страницу, чтобы увидеть изменения.

 npm install --save-dev webpack-dev-server 

package.json

  { ... "scripts": { - "develop": "webpack --watch --config webpack.dev.js", + "develop": "webpack-dev-server --config webpack.dev.js", } ... } 
 > npm run develop  「wds」: Project is running at http://localhost:8080/  「wds」: webpack output is served from / 

Откройте http: // localhost: 8080 / в браузере и внесите изменения в один из файлов JavaScript или CSS. Вы должны увидеть его сборку и обновление автоматически.

HotModuleReplacement

Плагин HotModuleReplacement идет на один шаг дальше, чем Live Reloading, и заменяет модули во время выполнения без обновления . При правильной настройке это экономит огромное количество времени при разработке одностраничных приложений. Там, где у вас много состояний на странице, вы можете вносить постепенные изменения в компоненты, и только измененные модули заменяются и обновляются.

webpack.dev.js

 + const webpack = require('webpack') const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'development', + devServer: { + hot: true + }, + plugins: [ + new webpack.HotModuleReplacementPlugin() + ], ... } 

Теперь нам нужно принять измененные модули из нашего кода для повторной инициализации.

SRC / app.js

 + if (module.hot) { + module.hot.accept() + } ... 

Примечание. Когда горячая замена модуля включена, для module.hot задано значение true для разработки и false для производства, поэтому они удаляются из комплекта.

Перезапустите сборку и посмотрите, что произойдет, если мы сделаем следующее:

  • Нажмите Открыть чат
  • Добавить нового человека в модуль people.js
  • Нажмите Открыть чат еще раз.

Вот что происходит:

  1. При нажатии на chat.js Открыть чат» модуль chat.js и инициализируется.
  2. HMR обнаруживает изменение people.js
  3. module.hot.accept() в index.js вызывает index.js всех модулей, загруженных этим index.js
  4. При повторном нажатии на « Открыть чат» chat.init() с кодом из обновленного модуля.

Замена CSS

Давайте изменим цвет кнопки на красный и посмотрим, что произойдет:

SRC / app.scss

  button { ... - background: #24b47e; + background: red; ... } 

Теперь мы видим мгновенные обновления наших стилей без потери состояния. Это значительно улучшенный опыт разработчика! И это похоже на будущее.

HTTP / 2

Одним из основных преимуществ использования модуля пакетов, такого как Webpack, является то, что он может помочь вам повысить производительность, предоставляя вам контроль над тем, как ресурсы создаются и затем выбираются на клиенте. В течение многих лет считалось, что лучше объединять файлы, чтобы уменьшить количество запросов, которые необходимо выполнить на клиенте. Это все еще действует, но HTTP / 2 теперь позволяет доставлять несколько файлов за один запрос , поэтому конкатенация больше не является серебряной пулей. Ваше приложение может получить выгоду от кэширования множества небольших файлов. Затем клиент может получить один измененный модуль, вместо того чтобы снова получать весь пакет, в основном с тем же содержимым.

Создатель Webpack, Тобиас Копперс , написал информативный пост, объясняющий, почему пакетирование все еще важно, даже в эпоху HTTP / 2.

Подробнее об этом читайте в Webpack & HTTP / 2 .

К вам

Я надеюсь, что вы нашли это введение в Webpack полезным и смогли начать использовать его с большим эффектом. Может потребоваться некоторое время, чтобы обдумать конфигурацию Webpack, загрузчики и плагины, но изучение того, как работает этот инструмент, окупится.

В настоящее время ведется работа над документацией для Webpack 4, но она действительно хорошо составлена. Я настоятельно рекомендую прочитать Концепции и Руководства для получения дополнительной информации. Вот еще несколько тем, которые могут вас заинтересовать:

Является ли Webpack 4 вашим модулем выбора? Позвольте мне знать в комментариях ниже.