Параметр devServer.static в Webpack Dev Server отвечает
за раздачу статических файлов без участия механизма сборки Webpack.
Через него обслуживаются директории с HTML-файлами, изображениями,
шрифтами, favicon, mock-данными и любыми другими ресурсами, которые
должны быть доступны браузеру напрямую.
Механизм особенно важен при локальной разработке, когда часть файлов не импортируется через JavaScript и не проходит через loaders/plugins.
Простейшая конфигурация:
module.exports = {
devServer: {
static: './public'
}
};
В этом случае Webpack Dev Server начинает обслуживать содержимое
директории public.
Например:
public/
├── index.html
├── favicon.ico
├── images/
│ └── logo.png
Файлы становятся доступны по адресам:
http://localhost:8080/index.html
http://localhost:8080/favicon.ico
http://localhost:8080/images/logo.png
devServer.static от output.pathЧастая ошибка — путать раздачу статики с каталогом сборки.
output.pathoutput: {
path: path.resolve(__dirname, 'dist')
}
Определяет директорию, куда Webpack складывает результаты сборки.
devServer.staticdevServer: {
static: './public'
}
Раздаёт файлы напрямую с диска, без обработки Webpack.
Webpack предназначен для обработки модульной системы:
Но многие файлы:
Типичные примеры:
robots.txt
sitemap.xml
manifest.json
favicon.ico
static JSON
mock API files
Для них используется devServer.static.
Вместо объекта можно передать строку:
devServer: {
static: './public'
}
Webpack автоматически интерпретирует это как:
devServer: {
static: {
directory: path.join(__dirname, 'public')
}
}
На практике чаще используется объект:
const path = require('path');
module.exports = {
devServer: {
static: {
directory: path.join(__dirname, 'public')
}
}
};
directoryГлавный параметр.
static: {
directory: path.join(__dirname, 'public')
}
Определяет физическую директорию на диске.
Рекомендуется всегда использовать path.join() или
path.resolve().
Правильно:
directory: path.resolve(__dirname, 'public')
Нежелательно:
directory: './public'
Причина — зависимость от текущей рабочей директории процесса.
Webpack Dev Server поддерживает массив.
module.exports = {
devServer: {
static: [
{
directory: path.join(__dirname, 'public')
},
{
directory: path.join(__dirname, 'assets')
}
]
}
};
Теперь сервер обслуживает обе директории.
При совпадении имён используется первый найденный файл.
Пример:
public/logo.png
assets/logo.png
Если public указана первой, браузер получит именно этот
файл.
publicPathПозволяет изменить URL-префикс.
module.exports = {
devServer: {
static: {
directory: path.join(__dirname, 'public'),
publicPath: '/static/'
}
}
};
Теперь:
public/logo.png
будет доступен по адресу:
http://localhost:8080/static/logo.png
Это позволяет:
publicPathdevServer: {
static: [
{
directory: path.join(__dirname, 'images'),
publicPath: '/img/'
},
{
directory: path.join(__dirname, 'fonts'),
publicPath: '/fonts/'
}
]
}
Результат:
/images/logo.png → /img/logo.png
/fonts/main.woff → /fonts/main.woff
watchОпределяет отслеживание изменений.
devServer: {
static: {
directory: path.join(__dirname, 'public'),
watch: true
}
}
При изменении файлов браузер автоматически обновляется.
Иногда наблюдение за файлами создаёт лишнюю нагрузку.
watch: false
Особенно актуально:
Можно передать дополнительные параметры chokidar.
devServer: {
static: {
directory: path.join(__dirname, 'public'),
watch: {
ignored: '*.psd',
usePolling: false
}
}
}
watchignoredИгнорирование файлов.
watch: {
ignored: /node_modules/
}
usePollingИспользование polling вместо native watchers.
watch: {
usePolling: true
}
Полезно:
intervalИнтервал polling.
watch: {
usePolling: true,
interval: 1000
}
serveIndexПозволяет отображать список файлов директории.
devServer: {
static: {
directory: path.join(__dirname, 'public'),
serveIndex: true
}
}
При переходе в каталог браузер показывает список файлов.
public/
├── docs/
│ ├── a.txt
│ └── b.txt
URL:
http://localhost:8080/docs/
Покажет файловый индекс.
serveIndex: false
Чаще используется именно этот вариант.
staticOptionsПередаёт параметры библиотеке serve-static.
devServer: {
static: {
directory: path.join(__dirname, 'public'),
staticOptions: {
extensions: ['html']
}
}
}
Теперь URL:
/about
может автоматически открыть:
about.html
Через staticOptions.setHeaders.
devServer: {
static: {
directory: path.join(__dirname, 'public'),
staticOptions: {
setHeaders(res) {
res.setHeader('X-Test', 'webpack');
}
}
}
}
Статические ресурсы можно кэшировать.
staticOptions: {
maxAge: '1d'
}
const path = require('path');
module.exports = {
devServer: {
static: {
directory: path.resolve(__dirname, 'public'),
publicPath: '/assets/',
watch: {
ignored: /\.psd$/
},
serveIndex: false,
staticOptions: {
maxAge: '1d'
}
}
}
};
Частая схема:
src/
public/
dist/
Где:
src — исходники;dist — сборка;public — статические файлы.module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
})
],
devServer: {
static: {
directory: path.join(__dirname, 'public')
}
}
};
devServer.staticРаздаёт файлы напрямую.
Файлы физически не копируются.
Работает только в dev server.
CopyWebpackPluginКопирует файлы в output directory.
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
to: 'dist'
}
]
})
Работает во время сборки.
Частая конфигурация:
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
plugins: [
!isDev &&
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
to: '.'
}
]
})
].filter(Boolean),
devServer: {
static: {
directory: path.join(__dirname, 'public')
}
}
};
Для Single Page Application часто используется:
historyApiFallback: true
Полная конфигурация:
devServer: {
historyApiFallback: true,
static: {
directory: path.join(__dirname, 'public')
}
}
historyApiFallback и staticПорядок обработки:
index.htmlURL:
/profile/settings
Если файла нет:
public/profile/settings
сервер отдаст:
index.html
Одна из самых популярных задач.
public/favicon.ico
Конфигурация:
devServer: {
static: {
directory: path.join(__dirname, 'public')
}
}
Файл автоматически доступен:
/favicon.ico
public/api/users.json
Доступ:
http://localhost:8080/api/users.json
Это удобно:
public/images/banner.jpg
HTML:
<img src="/images/banner.jpg">
import banner from './banner.jpg';
В этом случае файл проходит через asset modules.
devServer.staticПодходит для:
importПодходит для:
devServer.static почти не нагружает Webpack, потому
что:
Это особенно важно для:
Ошибка:
directory: 'public'
При запуске из другой директории файлы не находятся.
publicPathoutput: {
publicPath: '/'
}
devServer: {
static: {
publicPath: '/'
}
}
Иногда это вызывает путаницу между webpack assets и обычной статикой.
Файлы из static:
static вместо asset modulesНекоторые разработчики складывают все изображения в
public, теряя:
contentBaseДо Webpack Dev Server v4 использовался параметр:
devServer: {
contentBase: path.join(__dirname, 'public')
}
В новых версиях:
devServer: {
static: {
directory: path.join(__dirname, 'public')
}
}
const path = require('path');
module.exports = {
mode: 'development',
output: {
publicPath: '/'
},
devServer: {
port: 3000,
hot: true,
compress: true,
historyApiFallback: true,
static: [
{
directory: path.resolve(__dirname, 'public'),
publicPath: '/',
watch: {
ignored: /\.tmp$/
},
serveIndex: false,
staticOptions: {
maxAge: '1h'
}
},
{
directory: path.resolve(__dirname, 'mock'),
publicPath: '/api/',
watch: true
}
]
}
};