Webpack. Начало работы, часть 1 из 2
06.07.2022
Теги: Frontend • JavaScript • Node.js • Web-разработка • Конфигурация • Настройка • Установка • ШаблонСайта
По большей части, сайты больше не пишутся на чистом HTML с небольшим количеством JavaScript — часто они создаются только с помощью JavaScript. Поэтому возникает необходимость в сборке, минификации и транспиляции кода. Вот здесь webpack и приходит на помощь разработчику.
Вебпак — это сборщик модулей. Он служит для упаковки кода для дальнейшего использования браузером. Позволяет использовать последние возможности JavaScript с помощью Babel или использовать TypeScript и преобразовывать его в кроссбраузерный минифицированный код. Он также позволяет импортировать статические ресурсы в JavaScript.
Для разработчиков webpack также предоставляет сервер для разработки, который умеет обновлять модули и стили на лету при сохранении файла. Инструменты командной строки, такие как «vue create» и «create-react-app» используют вебпак за сценой, но можно создать собственную настройку webpack для указанных фреймворков.
Создание проекта
Создаем директорию проекта и инициализируем его:
$ mkdir webpack-config-example $ cd webpack-config-example $ npm init -y
Немного подредактируем файл package.json
:
{ "name": "webpack-config-example", "version": "1.0.0", "description": "Webpack config file example", "private": true, "scripts": { "build": "webpack" }, "keywords": ["webpack", "config", "javascript", "frontend"], "author": "Evgeniy Tokmakov", "license": "ISC" }
Устанавливаем два пакета как зависимости для разработки:
$ npm install webpack webpack-cli --save-dev
Создаем директорию src
, а внутри нее — директории js
, css
, img
, font
, data
. Внутри src/js
создаем файл index.js
.
console.log('Webpack config file example')
Создаем файл webpack.config.js
в корневой директории проекта. Файл экспортирует объект, который содержит все настройки сборщика.
module.exports = {}
Точка входа
Сколько бы модулей не содержало приложение, всегда имеется единственная точка входа.
const path = require('path') module.exports = { entry: { main: path.resolve(__dirname, 'src/js/index.js'), }, }
const path = require('path') module.exports = { // для одной точки входа код можно сократить entry: path.resolve(__dirname, 'src/js/index.js'), }
Точка выхода
Точка выхода — это директория, в которую помещаются сформированные вебпаком файлы.
const path = require('path') module.exports = { entry: { main: path.resolve(__dirname, 'src/js/index.js'), }, output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, }
./src/index.js
, значение по умолчанию точки выхода — ./dist/main.js
.
Когда есть несколько точек входа, то файл dist/bundle.js
будет перезаписан, потому что имя одинаковое. В этом случае можно использовать шаблон, чтобы использовать ключ объекта entry
в имени выходного файла.
module.exports = { entry: { app: '.....', admin: '.....', }, output: { filename: '[name].bundle.js', path: __dirname + '/dist', }, } // выходные файлы ./dist/app.js и ./dist/admin.js
В принципе, минимальная конфигурация готова, так что можно запустить сборку:
$ npm run build
Мы получим предупреждение, что не установлен режим — development
или production
.
module.exports = { mode: 'development', /* .......... */ }
Наблюдение за файлами
При использовании опции watch
вебпак будет отслеживать изменения файлов и повторно запускать сборку.
module.exports = { mode: 'development', watch: true, watchOptions: { ignored: /node_modules/, // не отслеживать node_modules poll: 1000, // проверять изменения каждую секунду }, /* .......... */ }
watchOptions.poll
для наблюдения за файлами. Эта опция помогает решить проблемы с наблюдением при спользовании NFS, VirtualBox и Docker.
Плагины
Плагины делает вебпак очень гибким. Они используются как самим вебпаком, так и сторонними расширениями. Некоторые плагины используются почти в каждом проекте.
1. Плагин html-webpack-plugin
У нас есть готовая сборка, но она бесполезна без html-разметки, которая загрузит сборку в качестве скрипта. Поскольку нам нужно, чтобы такой html-файл генерировался автоматически, используем плагин html-webpack-plugin
.
$ npm install html-webpack-plugin --save-dev
Создаем файл фаблона index.html
в директории src
. В шаблон добавляем две переменные, которые определим в настройках плагина.
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <h1><%= htmlWebpackPlugin.options.title %></h1> <pre><%= htmlWebpackPlugin.options.content %></pre> </body> </html>
const path = require('path') const fs = require('fs') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'development', entry: { main: path.resolve(__dirname, 'src/js/index.js'), }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].bundle.js', }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack config example', content: fs.readFileSync('webpack.config.js', 'utf8'), template: path.resolve(__dirname, 'src/index.html'), // файл шаблона filename: 'index.html', // выходной файл }), ], }
Запускаем сборку. Директория dist
теперь содержит файл index.html
с подключенным в нем скриптом main.bundle.js
и подставленными значениями переменных.
2. Плагин clean-webpack-plugin
Установим clean-webpack-plugin
, очищающий директорию dist
при каждой сборке проекта. Это позволяет автоматически удалять старые файлы, ставшие ненужными.
$ npm install clean-webpack-plugin --save-dev
/* .......... */ const { CleanWebpackPlugin } = require('clean-webpack-plugin') module.exports = { /* .......... */ plugins: [ /* .......... */ new CleanWebpackPlugin(), ], }
Но этот плагин уже устарел, потому что вебпак сам умеет очищать директорию dist
с помощью опции output.clean
:
module.exports = { /* .......... */ output: { path: path.resolve(__dirname, 'dist'), filename: '[name].bundle.js', clean: true, }, /* .......... */ }
3. Плагин copy-webpack-plugin
Копирует отдельные файлы и целые директории, которые уже существуют, в выходную директорию проекта. Давайте разместим в директории src
иконку favicon.png
, добавим в html-шаблон ссылку на эту иконку, и скопируем файл при сборке в директорию dist
.
$ npm install copy-webpack-plugin --save-dev
/* .......... */ const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { /* .......... */ plugins: [ /* .......... */ new CleanWebpackPlugin(), new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, 'src/favicon.png'), to: path.resolve(__dirname, 'dist') }, ], }), ], }
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><%= htmlWebpackPlugin.options.title %></title> <link rel="icon" href="favicon.png" type="image/png"> </head> <body> <h1><%= htmlWebpackPlugin.options.title %></h1> <pre><%= htmlWebpackPlugin.options.content %></pre> </body> </html>
Модули и загрузчики
По умолчанию, при создании графа зависимостей на основе import
или require
, вебпак способен обрабатывать только javascript и json файлы. Для работы с css-стилями, изображениями, шрифтами — предназначены модули и загрузчики. Загрузчики описывают, как обрабатывать те файлы, с которыми webpack не умеет работать — и включать их в конечную сборку.
1. Загрузчик raw-loader
Позволяет импортировать файл как строку. Давайте создадим файл src/data/webpack.txt
и запишем в него строку «Это пример файла конфигурации Webpack». Установим загрузчик raw-loader
, пропишем его в файле конфигурации, потом в файле src/js/index.js
импортируем файл как строку и покажем на странице.
$ npm install raw-loader --save-dev
module.exports = { /* .......... */ module: { rules: [ { test: /\.txt$/i, // простой текст use: 'raw-loader', type: 'javascript/auto' }, ], } }
import data from '../data/webpack.txt' document.body.insertAdjacentHTML('beforeend', `<p>${data}</p>`)
Обратите внимание на type
в значении javascript/auto
— в пятой версии Webpack нет необходимости использовать внешние загрузчики raw-loader
, file-loader
, url-loader
. При использовании этих устаревших загрузчиков нужно установить type
в значение javascript/auto
, чтобы избежать повторной обработки встроенными загрузчиками вебпака.
И давайте посмотрим, как импортировать строку с использованием встроенного загрузчика, без установки raw-loader
:
module.exports = { /* .......... */ module: { rules: [ { test: /\.txt$/i, // простой текст type: 'asset/source' }, ], } }
2. Загрузчик file-loader
Позволяет импортировать файл (возвращая URL этого файла) и отправляет этот файл в выходной каталог. Давайте поместим файл логотипа logo.png
в директории src/img
, установим загрузчик file-loader
, пропишем его в файле конфигурации, а потом покажем изображение на странице.
$ npm install file-loader --save-dev
module.exports = { /* .......... */ module: { rules: [ { test: /\.txt$/i, // простой текст type: 'asset/source' }, { test: /\.(png|jpe?g|gif|webp)$/i, // изображение use: [ { loader: 'file-loader', options: { outputPath: 'img' // все изображения в dist/img }, }, ], type: 'javascript/auto' }, ], } }
import data from '../data/webpack.txt' import logo from '../img/logo.png' document.body.insertAdjacentHTML('beforeend', `<p>${data}</p>`) document.body.insertAdjacentHTML('afterbegin', `<img src="${logo}" alt="">`)
И давайте посмотрим, как импортировать строку с использованием встроенного загрузчика, без установки file-loader
:
module.exports = { /* .......... */ module: { rules: [ { test: /\.txt$/i, // простой текст type: 'asset/source' }, { test: /\.(png|jpe?g|gif|webp)$/i, // изображение type: 'asset/resource', generator: { filename: 'img/[hash][ext][query]' } }, ], } }
3. Загрузчик url-loader
Загрузчик url-loader
работает как file-loader
, но может возвращать DataURL
вместо URL
, если размер файла меньше заданного ограничения в байтах. Давайте поместим маленькое изображение icon.png
в директорию src/img
, установим загрузчик url-loader
, пропишем его в файле конфигурации, а потом покажем это изображение на странице.
module.exports = { /* .......... */ module: { rules: [ { test: /\.txt$/i, // простой текст type: 'asset/source' }, { test: /\.(png|jpe?g|gif|webp)$/i, // изображение use: [ { loader: 'url-loader', options: { limit: 8192, outputPath: 'img', // все изображения в dist/img }, }, ], type: 'javascript/auto' }, ], } }
import data from '../data/webpack.txt' import logo from '../img/logo.png' import icon from '../img/icon.png' document.body.insertAdjacentHTML('beforeend', `<p>${data}</p>`) document.body.insertAdjacentHTML('afterbegin', `<img src="${logo}" alt="">`) document.body.insertAdjacentHTML('beforeend', `<img src="${icon}" alt="">`)
Загрузчик file-loader
больше не нужен, поскольку его работу теперь выполняет url-loader
. В результате на странице у нас будут два изображения:
<img src="/img/56ee55657feb4c5d509da1359ac4a419.png" alt=""> .......... <img src="data:image/png;base64,iVBORw0KGgoAAAAN..." alt="">
И давайте посмотрим, как импортировать изображение с использованием встроенного загрузчика, без установки url-loader
:
module.exports = { /* .......... */ module: { rules: [ { test: /\.txt$/i, // простой текст type: 'asset/source' }, { test: /\.(png|jpe?g|gif|webp)$/i, // изображение type: 'asset', generator: { filename: 'img/[hash][ext][query]' } }, ], } }
<img src="/img/56ee55657feb4c5d509d.png" alt=""> .......... <img src="data:image/png;base64,iVBORw…" alt="">
При использовании asset
, если размер файла больше 8кб, то применяется asset/resource
(изображение в директории dist/img
), в противном случае — asset/inline
(изображение в атрибуте src="…"
). Ограничение на размер файла можно задать самому, если значение по умолчанию не подходит.
module.exports = { /* .......... */ module: { rules: [ { test: /\.txt$/i, // простой текст type: 'asset/source' }, { test: /\.(png|jpe?g|gif|webp)$/i, // изображение type: 'asset', generator: { filename: 'img/[hash][ext][query]' } parser: { dataUrlCondition: { maxSize: 4096 // ограничение 4kb } } }, ], } }
<img src="/img/56ee55657feb4c5d509d.png" alt=""> .......... <img src="/img/cb1ea424ee012d4721cf.png" alt="">
asset/inline
— то все изображения будут DataURL
, но это не слишком удачное решение.
4. Загрузчики style-loader и css-loader
Загрузчик css-loader
позволяет импортировать css-файл, а загрузчик style-loader
динамически добавляет в выходной html-файл тег <style>
. Давайте установим эти два загрузчика, пропишем их в файле конфигурации, создадим css-файл style.css
в директории src/css
и импортируем стили в файле src/js/index.js
.
$ npm install css-loader --save-dev
$ npm install style-loader --save-dev
body {
margin: 0;
padding: 0;
background-color: #ccc;
}
module.exports = { /* .......... */ module: { rules: [ /* .......... */ { test: /\.css$/, // файл css-стилей use: ['style-loader', 'css-loader'] } ], } }
import data from '../data/webpack.txt' import logo from '../img/logo.png' import icon from '../img/icon.png' import '../css/style.css' document.body.insertAdjacentHTML('beforeend', `<p>${data}</p>`) document.body.insertAdjacentHTML('afterbegin', `<img src="${logo}" alt="">`) document.body.insertAdjacentHTML('beforeend', `<img src="${icon}" alt="">`)
5. Загрузчик postcss-loader
Загрузчик postcss-loader
использует процессор PostCSS для трансформации css-стилей. Сам PostCSS никаких трансформаций не делает, только создает из css-стилей js-объекты, а потом из js-объектов — обратно css-стили. Трансформацией занимаются плагины для PostCSS — в момент, когда созданы js-объекты из входных стилей, но до момента, когда еще не созданы выходные стили. Трансформации могут быть самые разные, например — добавление префиксов к css-правилам с помощью плагина autoprefixer
.
Устанавливаем postcss-loader
и postcss-preset-env
(полезные настройки для процессора postcss):
$ npm install postcss-loader postcss-preset-env --save-dev
module.exports = { /* .......... */ module: { rules: [ /* .......... */ { test: /\.css$/, // файл css-стилей use: [ 'style-loader', 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: { 'postcss-preset-env': { browsers: 'last 3 versions', }, }, }, }, }, ], }, ], } }
/* входные стили */
body {
margin: 0;
padding: 0;
background-color: #ccc;
box-sizing: border-box;
}
/* выходные стили */
body {
margin: 0;
padding: 0;
background-color: #ccc;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
- Webpack. Начало работы, часть 2 из 2
- Расширение «ESLint» для VS Code, часть 2 из 2
- Расширение «ESLint» для VS Code, часть 1 из 2
- Расширение «Prettier — Code formatter» для VS Code
- Магазин на JavaScript, часть 19 из 19. Редактирование характеристик и рефакторинг приложения
- Магазин на JavaScript, часть 18 из 19. Панель управления: редактирование категорий и брендов
- Магазин на JavaScript, часть 17 из 19. Панель управления: список заказов, категорий и брендов
Поиск: Frontend • JavaScript • Node.js • Web-разработка • Конфигурация • Настройка • Установка • Шаблон сайта • Webpack • Сборка