Статьи

Руководство для начинающих по Webpack

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

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

Примечание: в этом уроке я использовал webpack 4.30.

SitePoint Premium предоставляет вам полную коллекцию книг, посвященных основам разработки, таким как Pug, Gulp, Git и другим. Присоединяйтесь сейчас .

Что такое Webpack?

По своей сути webpack представляет собой пакет статических модулей. В конкретном проекте веб-пакет обрабатывает все файлы и ресурсы как модули. Под капотом он опирается на граф зависимостей. График зависимостей описывает, как модули связаны друг с другом, используя ссылки (операторы require и import ) между файлами. Таким образом, webpack статически обходит все модули для построения графа и использует его для генерации одного пакета (или нескольких пакетов) — файла JavaScript, содержащего код из всех модулей, объединенных в правильном порядке. «Статически» означает, что, когда webpack создает свой граф зависимостей, он не выполняет исходный код, а объединяет модули и их зависимости в пакет. Это может быть включено в ваши файлы HTML.

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

Основные понятия Webpack

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

  • Вступление. Точка входа — это модуль, который веб-пакет использует для построения своего внутреннего графа зависимостей. Оттуда он определяет, от каких других модулей и библиотек зависит эта точка входа (прямо или косвенно), и включает их в граф, пока не останется никакой зависимости. По умолчанию свойство entry имеет значение ./src/index.js , но мы можем указать другой модуль (или даже несколько модулей) в файле конфигурации веб-пакета.

  • Выход. Свойство output указывает веб-пакету, куда отправлять комплект (ы) и какое имя использовать для этого файла (ов). Значением по умолчанию для этого свойства является ./dist/main.js для основного пакета и ./dist для других сгенерированных файлов, таких как изображения, например. Конечно, мы можем указать различные значения в конфигурации в зависимости от наших потребностей.

  • Погрузчики. По умолчанию webpack понимает только файлы JavaScript и JSON. Для обработки других типов файлов и преобразования их в действительные модули в веб-пакете используются загрузчики. Загрузчики преобразуют исходный код модулей, отличных от JavaScript, что позволяет нам предварительно обрабатывать эти файлы перед их добавлением в граф зависимостей. Например, загрузчик может преобразовывать файлы из языка CoffeeScript в JavaScript или встроенные изображения в URL-адреса данных. С помощью загрузчиков мы можем даже импортировать CSS-файлы прямо из наших модулей JavaScript.

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

  • Режим. Как правило, когда мы разрабатываем наше приложение, мы работаем с двумя типами исходного кода — один для сборки разработки и один для сборки производства. Webpack позволяет нам определить, какой из них мы хотим создать, изменив параметр mode на development , production или none . Это позволяет веб-пакету использовать встроенные оптимизации, соответствующие каждой среде. Значением по умолчанию является производство . Режим none означает, что не будут использоваться никакие опции оптимизации по умолчанию. Чтобы узнать больше об опциях, которые использует веб-пакет в режиме разработки и производства , посетите страницу конфигурации режима .

Как работает Webpack

В этом разделе мы рассмотрим, как работает веб-пакет. Даже простой проект содержит файлы HTML, CSS и JavaScript. Кроме того, он может содержать ресурсы, такие как шрифты, изображения и т. Д. Таким образом, типичный рабочий процесс веб-пакета включает настройку файла index.html с соответствующими ссылками CSS и JS и необходимыми ресурсами. Кроме того, если у вас много модулей CSS и JS, которые зависят друг от друга, их необходимо оптимизировать и правильно объединить в одном модуле, готовом к производству.

Чтобы сделать все это, веб-пакет опирается на конфигурацию. Хотя webpack 4 поставляется с разумными значениями по умолчанию, для любого нетривиального проекта вам необходимо предоставить специальный файл конфигурации webpack.config.js , в котором описывается, как файлы и ресурсы должны быть преобразованы и какие выходные данные должны быть сгенерированы. Этот файл может быстро стать довольно монолитным, что затрудняет понимание того, как веб-пакет выполняет свою работу, если вы не знаете основные концепции его работы.

На основе предоставленной конфигурации webpack запускается с точек входа и разрешает каждый модуль, с которым он сталкивается, при построении графа зависимостей. Если модуль содержит зависимости, процесс выполняется рекурсивно для каждой зависимости до завершения обхода. Затем веб-пакет объединяет все модули проекта в небольшое количество пакетов — обычно только один — для загрузки браузером.

Начиная

Примечание: вы можете найти файлы для нашего проекта в репозитории GitHub .

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

Для начала мы создадим новый каталог и переключимся на него. Затем мы инициализируем новый проект:

 mkdir learn-webpack cd learn-webpack npm init -y 

Далее нам нужно установить webpack и webpack CLI локально:

 npm install webpack webpack-cli --save-dev 

Теперь содержимое сгенерированного package.json должно быть примерно таким:

 { "name": "learn_webpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.30.0", "webpack-cli": "^3.3.0" } } 

Помимо комплектующих модулей, веб-пакет может использоваться как простой бегун задач. Мы можем создавать задачи веб-пакета, включив имя нашей задачи и ее инструкции в разделе scripts package,json файл. Давайте попробуем это сейчас. Откройте package.json и измените объект scripts следующим образом:

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

В свойстве scripts , webpack позволяет нам ссылаться на локально установленные пакеты npm по их именам. Мы используем это и флаг --mode для определения задач dev и build , которые будут запускать webpack в режимах разработки ( npm run dev ) и производства ( npm run build ) соответственно.

Прежде чем тестировать только что созданные задачи, давайте создадим каталог src и поместим в index.js файл index.js чтобы он содержал console.log("Hello webpack"); , Теперь мы уже можем запустить задачу dev для запуска веб-пакета в режиме разработки:

 $ npm run dev > [email protected] dev C:\Users\User\Webpack\learn_webpack > webpack --mode development Hash: 5bb3bdc1efd7b7f4b627 Version: webpack 4.30.0 Time: 226ms Built at: 2019-04-16 17:48:32 Asset Size Chunks Chunk Names main.js 3.8 KiB main [emitted] main Entrypoint main = main.js [./src/index.js] 27 bytes {main} [built] 

Большой! Работает как положено. Но чтобы убедиться, что мы получаем правильный вывод, нам нужно отобразить результат в браузере. Для этого давайте создадим файл index.html в каталоге dist :

 <!doctype html> <html> <head> <title>Getting Started</title> </head> <body> <script src="main.js"></script> </body> </html> 

Теперь, если мы откроем файл в браузере, мы увидим сообщение Hello Webpack в консоли.

Все идет нормально. Но в некоторых случаях написание нашего файла index.html вручную может быть проблематичным. Например, если мы изменим имя нашей точки входа, сгенерированный пакет будет переименован, но наш файл index.html все равно будет ссылаться на старое имя. Поэтому нам нужно обновлять наш HTML-файл вручную каждый раз, когда мы переименовываем точку входа или добавляем новую. К счастью, мы можем легко это исправить с помощью html-webpack-plugin . Давайте установим это сейчас:

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

На данный момент, чтобы активировать плагин, нам нужно создать файл webpack.config.js в корневом каталоге со следующим содержимым:

 const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require('path'); module.exports = { plugins: [ new HtmlWebpackPlugin({ title: "Webpack Output", }), ], }; 

Как видите, чтобы активировать плагин веб-пакета, нам нужно включить его, а затем добавить в массив plugins . При необходимости мы также передаем параметры плагину.

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

 $ npm run build > [email protected] build C:\Users\User\Webpack\learn_webpack > webpack --mode production Hash: e56a796f5ccfebcc8270 Version: webpack 4.30.0 Time: 1088ms Built at: 2019-04-16 20:44:47 Asset Size Chunks Chunk Names index.html 183 bytes [emitted] main.js 956 bytes 0 [emitted] main Entrypoint main = main.js [0] ./src/index.js 27 bytes {0} [built] Child html-webpack-plugin for "index.html": 1 asset Entrypoint undefined = index.html [2] (webpack)/buildin/global.js 472 bytes {0} [built] [3] (webpack)/buildin/module.js 497 bytes {0} [built] + 2 hidden modules 

Давайте откроем index.html . Как мы видим, плагин автоматически создает для нас обновленный файл index.html , который использует опцию title из конфигурации:

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Webpack Output</title> </head> <body> <script type="text/javascript" src="main.js"></script></body> </html> 

Давайте теперь расширим наш проект и определим пользовательские имена для свойств entry и output . В webpack.config.js мы добавляем следующее перед свойством plugins :

 entry: './src/app.js', output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, 

Теперь мы создадим файл src/component.js :

 export default (text = "Hello webpack") => { const element = document.createElement("p"); element.innerHTML = text; return element; }; 

Затем мы переименовываем index.js в app.js чтобы отразить наши изменения, и поменяем его содержимое следующим:

 import component from "./component"; document.body.appendChild(component()); 

Теперь давайте запустим веб-пакет в производственном режиме:

 $ npm run build > [email protected] build C:\Users\User\Webpack\learn_webpack > webpack --mode production Hash: 9f78936f8a2a21061f0b Version: webpack 4.30.0 Time: 1689ms Built at: 2019-04-17 23:43:40 Asset Size Chunks Chunk Names index.html 190 bytes [emitted] main.bundle.js 1.04 KiB 0 [emitted] main Entrypoint main = main.bundle.js [0] ./src/app.js + 1 modules 227 bytes {0} [built] | ./src/app.js 79 bytes [built] | ./src/component.js 148 bytes [built] Child html-webpack-plugin for "index.html": 1 asset Entrypoint undefined = index.html [2] (webpack)/buildin/global.js 472 bytes {0} [built] [3] (webpack)/buildin/module.js 497 bytes {0} [built] + 2 hidden modules 

Давайте рассмотрим и уточним информацию из вывода веб-пакета. Начиная сверху, мы видим хэш сборки, версию веб-пакета и время, необходимое для выполнения сборки. Далее мы видим файлы, созданные в каталоге dist ( index.html и main.bundle.js ). Под ними мы видим модуль ввода ( app.js ) и его зависимость ( component.js ). Вывод после Child html-webpack-plugin for "index.html": относится к внутренней работе html-webpack-plugin и мы можем спокойно его игнорировать.

Итак, теперь в папке dist у нас есть новый сгенерированный main.bundle.js файл main.bundle.js . Если мы откроем index.html в браузере, мы увидим на странице Hello Webpack . Также, если мы проверим источник index.html , мы увидим, что значение свойства src в теге script обновляется до main.bundle.js .

Работа со скриптами

В этом разделе мы узнаем, как ES6 можно перенести в ES5-совместимый код, который работает во всех браузерах. Давайте начнем с запуска следующей команды:

 npm run dev -- --devtools false 

main.bundle.js давайте откроем main.bundle.js :

 /***/ "./src/component.js": /*!**************************!*\ !*** ./src/component.js ***! \**************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = ((text = "Hello webpack") => { const element = document.createElement("p"); element.innerHTML = text; return element; }); /***/ }) 

Как видите, современные функции ES6 (функция стрелки и объявление const ) из модуля component.js по умолчанию не преобразованы в ES5-совместимый код. Чтобы наш код работал в старых браузерах, мы должны добавить загрузчик Babel:

 npm install babel-loader @babel/core @babel/preset-env --save-dev 

Затем в webpack.config.js добавьте module после output свойства:

 module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] }, 

Когда мы определяем правила для загрузчика веб-пакетов, обычно нужно определить три основных свойства:

  • test , который описывает, какие файлы должны быть преобразованы.
  • exclude , который определяет файлы, которые не должны быть обработаны из загрузчика (ов), если у нас есть такие.
  • use , который указывает, какой загрузчик (и) следует использовать против соответствующих модулей.

Еще раз введите следующую команду:

 npm run dev -- --devtools false 

На этот раз код в main.bundle.js компилируется:

 /***/ "./src/component.js": /*!**************************!*\ !*** ./src/component.js ***! \**************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = (function () { var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "Hello webpack"; var element = document.createElement("p"); element.innerHTML = text; return element; }); /***/ }) 

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

Работа со стилями

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

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

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

Давайте добавим необходимую конфигурацию в webpack.config.js :

 { test: /\.css$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], }, 

Здесь важен порядок грузчиков. Они оцениваются в обратном порядке — то есть справа налево и снизу вверх.

Теперь давайте создадим файл src/style.css :

 p { color: red; } 

Затем мы импортируем его в app.js :

 import './style.css' 

Когда мы запустим webpack и затем откроем index.html , мы увидим сообщение Hello webpack красного цвета.

Управление активами

Чаще всего ваш проект будет содержать ресурсы, такие как изображения, шрифты и так далее. Здесь мы рассмотрим пример с изображениями. Для начала нам нужно установить загрузчик файлов:

 npm install file-loader --save-dev 

Далее мы добавим новое правило в webpack.config.js :

 { test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader' }, ], }, 

Теперь, чтобы протестировать загрузчик, мы создадим image-component.js в каталоге src со следующим содержимым:

 import image from "./image.png" const img = document.createElement("img") img.src = image document.body.appendChild(img) 

Здесь мы импортируем наше изображение как модуль и используем его для создания <img/> . Нам нужно поместить это изображение в каталог src .

Следующим app.js является импорт нашего компонента изображения в app.js :

 import './image-component' 

И вуаля. Теперь, когда мы запустим webpack и откроем страницу, мы должны увидеть изображение над сообщением Hello webpack .

webpack-dev-server процесс разработки с помощью webpack-dev-server

В настоящее время нам нужно перестраивать наш код каждый раз, когда мы вносим изменения. К счастью, веб-пакет предлагает веб-сервер с живой перезагрузкой, который автоматически создает и обновляет страницу. Чтобы установить это:

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

Нам нужно обновить наш dev скрипт в package.json , чтобы использовать сервер:

 "dev": "webpack-dev-server --mode development" 

Теперь давайте настроим сервер в webpack.config.js , добавив следующее свойство:

 devServer: { contentBase: './dist', open: true }, 

Это говорит webpack-dev-server обслуживать файлы из каталога dist и автоматически открывать страницу входа.

Теперь, если мы запустим webpack ( npm run dev ), мы должны увидеть, как страница автоматически открывается в браузере на localhost:8080 :

 i 「wds」: Project is running at http://localhost:8080/ i 「wds」: webpack output is served from / i 「wds」: Content not from webpack is served from ./dist 

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

Очистить вывод

По мере продвижения нашего проекта папка dist может стать довольно загроможденной. При каждой сборке webpack генерирует пакеты и помещает их в папку dist , но не отслеживает, какие файлы фактически используются вашим проектом. Поэтому рекомендуется очищать папку dist перед каждой сборкой, чтобы генерировались только используемые файлы. Для этого нам нужно установить и настроить clean-webpack-plugin :

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

В webpack.config.js :

 const CleanWebpackPlugin = require('clean-webpack-plugin'); ... plugins: [ ... new CleanWebpackPlugin() ], 

Теперь запустите webpack ( npm run build ) и проверьте папку dist . Теперь вы должны видеть только файлы, сгенерированные из сборки, без старых и неиспользуемых файлов. В нашем случае файл, который следует удалить, — это main.js

Вывод

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