Точка входа (entry) определяет модуль, с которого
Webpack начинает построение графа зависимостей. Именно с этого файла
начинается анализ импортов, подключение зависимостей, обработка
loader-ами и формирование итогового бандла.
Во время сборки Webpack:
import и require.Без точки входа Webpack не знает, с какого файла начинать обработку проекта.
module.exports = {
entry: './src/index.js'
};
В данном случае:
./src/index.js — главный файл приложения;Структура проекта:
project/
├── src/
│ ├── index.js
│ ├── app.js
│ └── utils.js
└── webpack.config.js
Файл index.js:
import './app';
import './utils';
Webpack начинает с index.js, затем подключает
app.js и utils.js.
entry по
умолчаниюЕсли свойство entry не указано, Webpack использует
значение:
./src/index.js
То есть такая конфигурация:
module.exports = {};
эквивалентна:
module.exports = {
entry: './src/index.js'
};
Если файла не существует, сборка завершится ошибкой.
entryСвойство entry поддерживает несколько форматов:
Наиболее простой вариант.
module.exports = {
entry: './src/main.js'
};
Используется в небольших приложениях с единой точкой запуска.
Webpack позволяет объединять несколько файлов в одну стартовую точку.
module.exports = {
entry: [
'./src/polyfills.js',
'./src/index.js'
]
};
Порядок имеет значение.
Сначала будет обработан:
./src/polyfills.js
а затем:
./src/index.js
Частое применение:
Пример:
entry: [
'core-js/stable',
'regenerator-runtime/runtime',
'./src/index.js'
]
Наиболее распространённый вариант для средних и крупных проектов.
module.exports = {
entry: {
app: './src/app.js',
admin: './src/admin.js'
}
};
Webpack создаст отдельный бандл для каждой точки входа.
При использовании:
output: {
filename: '[name].bundle.js'
}
результат:
dist/
├── app.bundle.js
└── admin.bundle.js
Несколько entry используются, когда в одном проекте существует несколько независимых интерфейсов.
Пример:
src/
├── public/
│ └── index.js
├── admin/
│ └── index.js
└── landing/
└── index.js
Конфигурация:
module.exports = {
entry: {
public: './src/public/index.js',
admin: './src/admin/index.js',
landing: './src/landing/index.js'
}
};
Каждая часть проекта будет собираться отдельно.
Многостраничные приложения часто используют несколько HTML-файлов.
Например:
dist/
├── index.html
├── admin.html
├── public.bundle.js
└── admin.bundle.js
В index.html:
<script src="public.bundle.js"></script>
В admin.html:
<script src="admin.bundle.js"></script>
Каждая точка входа может быть описана объектом.
module.exports = {
entry: {
app: {
import: './src/app.js'
}
}
};
Поле import содержит основной файл точки входа.
dependOnПозволяет переиспользовать общие зависимости между точками входа.
Пример:
module.exports = {
entry: {
shared: ['lodash'],
app: {
import: './src/app.js',
dependOn: 'shared'
},
admin: {
import: './src/admin.js',
dependOn: 'shared'
}
}
};
Webpack вынесет lodash в отдельный общий бандл.
Результат:
dist/
├── shared.js
├── app.js
└── admin.js
Это уменьшает размер итоговых файлов.
filenameПозволяет задать имя файла для конкретной точки входа.
module.exports = {
entry: {
app: {
import: './src/app.js',
filename: 'js/app.bundle.js'
}
}
};
Результат:
dist/
└── js/
└── app.bundle.js
runtimeОпределяет runtime chunk для entry.
module.exports = {
entry: {
app: {
import: './src/app.js',
runtime: 'runtime'
}
}
};
Webpack вынесет служебный runtime-код в отдельный файл.
layerИспользуется совместно с системой layers.
module.exports = {
entry: {
app: {
import: './src/app.js',
layer: 'main'
}
}
};
Позволяет группировать модули по логическим слоям.
chunkLoadingУправляет механизмом загрузки chunk-файлов.
module.exports = {
entry: {
app: {
import: './src/app.js',
chunkLoading: 'jsonp'
}
}
};
Варианты зависят от окружения:
jsonpimportrequireasync-nodeWebpack поддерживает функцию в качестве entry.
module.exports = {
entry: () => './src/app.js'
};
Функция вызывается перед началом сборки.
Пример автоматического поиска страниц:
const fs = require('fs');
const path = require('path');
const pagesDir = './src/pages';
const entries = {};
fs.readdirSync(pagesDir).forEach(file => {
const name = path.parse(file).name;
entries[name] = `${pagesDir}/${file}`;
});
module.exports = {
entry: entries
};
Если структура:
src/pages/
├── home.js
├── catalog.js
└── profile.js
Webpack автоматически создаст:
{
home: './src/pages/home.js',
catalog: './src/pages/catalog.js',
profile: './src/pages/profile.js'
}
Webpack поддерживает Promise.
module.exports = {
entry: async () => {
return {
app: './src/app.js'
};
}
};
Это полезно при:
Точка входа становится вершиной графа модулей.
Пример:
index.js
├── app.js
│ ├── api.js
│ └── ui.js
└── utils.js
Webpack анализирует все связи между файлами.
Даже если модуль подключён глубоко внутри дерева:
import helper from './helpers/math';
он всё равно попадёт в граф зависимостей.
При использовании нескольких точек входа может возникать дублирование.
Пример:
entry: {
app: './src/app.js',
admin: './src/admin.js'
}
Если оба файла импортируют:
import _ from 'lodash';
то lodash может попасть в оба бандла.
Webpack умеет автоматически выделять общий код.
module.exports = {
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
Webpack создаст общий chunk:
vendors.js
вместо дублирования библиотек.
Точки входа работают совместно с динамическими импортами.
Пример:
import('./dashboard');
Webpack создаст отдельный chunk для dashboard.
Главный entry-файл:
entry: './src/index.js'
загрузит дополнительный chunk только при необходимости.
Каждая entry имеет собственный runtime.
Runtime содержит:
При множественных entry runtime может дублироваться.
Оптимизация:
optimization: {
runtimeChunk: 'single'
}
Webpack создаст:
runtime.js
общий для всех entry.
Для одностраничных приложений обычно используется один entry.
entry: './src/index.js'
Пример структуры React-приложения:
src/
├── index.js
├── App.js
├── routes/
└── components/
Вся система начинается с index.js.
Многостраничные приложения используют несколько entry.
Пример интернет-магазина:
entry: {
home: './src/home.js',
product: './src/product.js',
cart: './src/cart.js',
checkout: './src/checkout.js'
}
Каждая страница получает собственный JavaScript-бандл.
При множественных entry обычно используется несколько экземпляров плагина.
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
chunks: ['home']
}),
new HtmlWebpackPlugin({
filename: 'admin.html',
chunks: ['admin']
})
]
Плагин автоматически подключает нужные бандлы.
Webpack Dev Server использует точки входа для внедрения Hot Module Replacement.
Во время разработки Webpack может автоматически добавлять служебные runtime-модули к entry.
Пример внутреннего преобразования:
entry: [
'webpack/hot/dev-server',
'./src/index.js'
]
Точка входа зависит от целевого окружения.
Для браузера:
target: 'web'
Для Node.js:
target: 'node'
В Node.js-сборке entry может выглядеть так:
entry: './server/index.js'
Webpack может собирать библиотеки.
module.exports = {
entry: './src/library.js',
output: {
library: 'MyLibrary',
libraryTarget: 'umd'
}
};
Точка входа становится публичным API библиотеки.
Webpack анализирует зависимости начиная с entry-файла.
Если модуль:
то он может быть исключён из сборки.
Пример:
// utils.js
export function sum() {}
export function multiply() {}
// index.js
import { sum } from './utils';
Функция multiply может не попасть в
production-бандл.
entry: './src/main.js'
Если файл отсутствует:
Module not found
Иногда используются абсолютные пути:
const path = require('path');
entry: path.resolve(__dirname, 'src/index.js')
Это уменьшает вероятность ошибок.
Проблема:
entry: {
app: './src/app.js'
}
и:
output: {
filename: 'bundle.js'
}
Несколько entry будут пытаться записать один файл.
Правильный вариант:
output: {
filename: '[name].js'
}
const path = require('path');
module.exports = {
mode: 'production',
entry: {
main: {
import: './src/main.js',
dependOn: 'vendor'
},
admin: {
import: './src/admin.js',
dependOn: 'vendor'
},
vendor: ['lodash']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all'
}
}
};
Результат сборки:
dist/
├── runtime.js
├── vendor.js
├── main.a1b2c3.js
└── admin.d4e5f6.js
Такая конфигурация: