Webpack формирует граф зависимостей начиная с точки входа (entry). В классическом варианте конфигурации точка входа задаётся как статическое значение:
module.exports = {
entry: './src/index.js'
};
В этом случае имя точки входа неявно определяется как
main, а итоговый бандл получает имя main.js
(если не переопределён output.filename).
При переходе к многостраничным приложениям, модульным архитектурам и динамической генерации сборок появляется необходимость управлять именами точек входа программно. Это приводит к использованию динамических entry-объектов, функций и шаблонов именования.
Самый простой способ задать несколько точек входа:
module.exports = {
entry: {
home: './src/home.js',
about: './src/about.js',
contact: './src/contact.js'
},
output: {
filename: '[name].bundle.js'
}
};
Ключи объекта (home, about,
contact) становятся именами точек входа. Webpack использует
их в качестве значения переменной [name].
Механизм подстановки:
[name] — имя entry[id] — внутренний идентификатор чанка[contenthash] — хэш содержимого[chunkhash] — хэш чанкаДинамические имена точек входа фактически опираются на генерацию этого объекта.
Конфигурация Webpack — это обычный Node.js модуль. Поэтому entry можно формировать программно:
const fs = require('fs');
const path = require('path');
const pagesDir = path.resolve(__dirname, 'src/pages');
const entries = fs.readdirSync(pagesDir).reduce((acc, page) => {
const pageName = page.replace('.js', '');
acc[pageName] = path.join(pagesDir, page);
return acc;
}, {});
module.exports = {
entry: entries,
output: {
filename: '[name].bundle.js'
}
};
В этом подходе имена точек входа полностью зависят от структуры файловой системы. Добавление нового файла автоматически создаёт новый entry без изменения конфигурации.
Webpack сам по себе не интерпретирует glob-шаблоны в entry, но это можно реализовать через вспомогательные библиотеки:
const glob = require('glob');
const path = require('path');
const entries = {};
glob.sync('./src/pages/**/*.js').forEach(file => {
const name = path.basename(file, '.js');
entries[name] = file;
});
module.exports = {
entry: entries,
output: {
filename: '[name].js'
}
};
Здесь динамическое имя точки входа формируется из имени файла. Это базовый паттерн для CMS, админ-панелей и multi-page приложений.
Webpack поддерживает функциональный entry:
module.exports = {
entry: () => {
return {
app: './src/app.js',
admin: './src/admin.js'
};
}
};
Функция может быть синхронной или асинхронной:
module.exports = {
entry: async () => {
const response = await fetchConfigFromApi();
return response.entries;
}
};
Такая модель используется в сложных сборочных системах, где конфигурация зависит от внешних факторов:
Ключевой элемент динамических entry — согласование с output:
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js'
}
Если имя entry формируется динамически, Webpack подставляет его в filename. Это означает:
Ошибка в именовании entry приводит к раздуванию кэша и дублированию бандлов.
Entry не следует путать с динамическими импортами
(import()).
Entry формирует начальные чанки:
entry: {
main: './src/main.js'
}
Dynamic import формирует асинхронные чанки:
import('./module.js').then(module => {
module.init();
});
Однако динамические имена entry часто используются вместе с code splitting:
entry: {
pageA: './src/pageA.js',
pageB: './src/pageB.js'
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
В этом случае Webpack:
Типичный кейс — генерация entry для страниц сайта:
const pages = [
'home',
'catalog',
'product',
'checkout'
];
const entry = pages.reduce((config, page) => {
config[page] = `./src/pages/${page}/index.js`;
return config;
}, {});
module.exports = {
entry,
output: {
filename: '[name]/bundle.js'
}
};
Здесь имя entry используется как сегмент пути:
dist/
home/bundle.js
catalog/bundle.js
Это приближает структуру сборки к структуре маршрутизации приложения.
Webpack предоставляет механизм контекстов:
const modules = require.context('./pages', true, /\.js$/);
const entries = modules.keys().reduce((acc, key) => {
const name = key.replace('./', '').replace('.js', '');
acc[name] = modules(key);
return acc;
}, {});
module.exports = {
entry: entries
};
Особенности:
Часто динамические имена точек входа зависят от окружения:
const isAdmin = process.env.BUILD_TARGET === 'admin';
module.exports = {
entry: isAdmin
? { admin: './src/admin.js' }
: { app: './src/app.js' }
};
Более сложный вариант:
const target = process.env.TARGET;
module.exports = {
entry: {
[target]: `./src/${target}/index.js`
}
};
Здесь имя entry совпадает с логическим именем сборки, что упрощает CI-конфигурации.
При динамической генерации entry возникает риск:
Решение — нормализация имени:
function normalizeName(filePath) {
return filePath
.replace('./src/', '')
.replace('/index.js', '')
.replace(/\//g, '-');
}
Пример:
./src/pages/user/profile/index.js → pages-user-profile
Такой подход гарантирует уникальность даже при глубокой вложенности.
Webpack строит граф модулей, но entry-имя становится корнем графа чанка. Это означает:
Пример:
entry: {
dashboard: './src/app.js'
}
Если переименовать:
entry: {
admin: './src/app.js'
}
модульный граф тот же, но выходной chunk — новый.
В production-сборках динамические имена требуют стабильности. Частые ошибки:
Корректный подход:
Реальные проекты часто используют гибрид:
const fs = require('fs');
const path = require('path');
const baseEntries = {
vendor: './src/vendor.js'
};
const pageEntries = fs.readdirSync('./src/pages')
.sort()
.reduce((acc, name) => {
acc[name] = `./src/pages/${name}/index.js`;
return acc;
}, {});
module.exports = {
entry: {
...baseEntries,
...pageEntries
}
};
Такое разделение позволяет:
При динамических entry почти всегда используется связка с HTML генераторами:
Пример логики:
Это делает имя entry центральной точкой всей сборочной системы.
Наиболее частые проблемы:
Типичная архитектурная ошибка — превращение entry в замену маршрутизатору приложения, что приводит к дублированию логики между бандлами.
При десятках и сотнях entry:
Решения:
Вместо динамической генерации на лету используется промежуточный слой:
manifest.json
{
"home": "./src/pages/home/index.js",
"about": "./src/pages/about/index.js"
}
Webpack конфиг:
const manifest = require('./manifest.json');
module.exports = {
entry: manifest
};
Такой подход переносит динамику из Webpack в отдельный этап сборки, повышая предсказуемость.