Shimming — техника обеспечения совместимости модулей, которые ожидают определённые глобальные переменные или API. В экосистеме Webpack shimming реализуется через ProvidePlugin, imports-loader и exports-loader, позволяя подключать legacy-библиотеки без изменения их исходного кода. По данным Webpack Documentation (2026), shimming остаётся ключевым инструментом для интеграции jQuery-плагинов и других зависимостей, не поддерживающих модульную систему.
Главное
Shimming — это программная техника, которая встраивает слой совместимости между кодом и окружением, не изменяя исходный код модуля. В контексте сборки JavaScript shimming решает проблему, когда модуль обращается к глобальным переменным (window.$, global.process), которые отсутствуют в модульном окружении.
Polyfill реализует отсутствующую функциональность с нуля, добавляя новые возможности в окружение. Например, core-js добавляет Array.prototype.flatMap для старых браузеров. Shim же перенаправляет существующие вызовы на доступные реализации или подменяет ожидаемые глобальные объекты. В Webpack ProvidePlugin автоматически подставляет import $ from 'jquery' везде, где встречается обращение к глобальной переменной $, не требуя изменений в коде.
Основное различие — в цели. Polyfill добавляет то, чего нет, а shim делает существующий код совместимым с окружением, в котором он выполняется. Выбор между ними зависит от того, какая проблема решается: отсутствие API или несовместимость интерфейсов.
Webpack обрабатывает каждый модуль как изолированную единицу с собственной областью видимости. Если библиотека обращается к глобальной переменной jQuery как к window.$, сборка завершится ошибкой, поскольку в модульном контексте этой переменной нет. ProvidePlugin решает проблему на этапе компиляции: при обнаружении идентификатора $ в коде плагин автоматически вставляет import $ from 'jquery' в начало файла.
// Исходный код (legacy-модуль обращается к глобальной jQuery)
$('.element').hide();
// После обработки ProvidePlugin (Webpack вставляет import)
import $ from 'jquery';
$('.element').hide();
Дополнительно imports-loader позволяет явно указать, какие зависимости должен получать модуль. Это полезно, когда библиотека использует this на верхнем уровне, ожидая, что this ссылается на window, а не на module.exports.
ProvidePlugin — встроенный плагин Webpack, который автоматически подгружает модули при обнаружении обращения к заданным идентификаторам. Конфигурация представляет собой объект, где ключ — имя переменной, а значение — путь к модулю и экспортируемое поле.
// webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
_: 'lodash',
'window.$': 'jquery',
}),
],
};
ProvidePlugin поддерживает точечный импорт через синтаксис массива. Например, [lodash, debounce] импортирует только функцию debounce из lodash, что уменьшает размер финального бандла. Это особенно важно для мобильных проектов, где каждый килобайт влияет на время загрузки.
imports-loader добавляет необходимые импорты в начало модуля, а exports-loader — задаёт экспортируемые значения для модулей, которые не используют module.exports явно. Эти лоадеры работают на уровне отдельных файлов, а не глобально, как ProvidePlugin.
// webpack.config.js — настройка imports-loader
module.exports = {
module: {
rules: [
{
test: /legacy-module\.js$/,
use: [
{
loader: 'imports-loader',
options: {
imports: [
'jquery',
'$',
],
},
},
],
},
],
},
};
exports-loader используется, когда библиотека присваивает значение глобальной переменной, но не экспортирует его через модульную систему. Лоадер извлекает значение и превращает его в модульный экспорт, что позволяет другим модулям импортировать его через import.
Shimming конфигурируется в webpack.config.js через комбинацию плагинов и лоадеров. Типовой сценарий включает ProvidePlugin для глобальных переменных и imports-loader для конкретных модулей, которые требуют изменения области видимости.
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
globalObject: 'this',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(?!legacy-lib)/,
use: [
{
loader: 'imports-loader',
options: {
type: 'commonjs',
imports: ['jquery', '$'],
},
},
],
},
],
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
],
};
Поле globalObject в output задаёт контекст для обращений к this на верхнем уровне. Для браузерного окружения значение 'this' ссылается на window, а для React Native или Node.js — на global. Выбор правильного значения предотвращает ошибки выполнения в целевой среде.
Shimming — мощный, но опасный инструмент. Неправильная настройка приводит к дублированию кода в бандле, конфликтам имён и неожиданным ошибкам выполнения. Разработчики часто забывают, что ProvidePlugin работает на этапе компиляции и не может обработать динамические обращения к переменным.
Если два плагина используют разные версии jQuery, ProvidePlugin подставит только одну из них, указанную первым в конфигурации. Вторая библиотека получит несовместимую версию, что вызовет трудноотлаживаемые ошибки. Решение — использовать exports-loader для каждой библиотеки с явным указанием версии или применять webpack.IgnorePlugin для исключения дублирующихся модулей.
Ещё одна распространённая ошибка — попытка зашифровать модули, которые используют CommonJS синхронные вызовы require в динамическом контексте. ProvidePlugin обрабатывает только статические идентификаторы, поэтому динамические обращения необходимо заменять вручную или использовать NormalModuleReplacementPlugin.
Неправильная настройка shimming может привести к значительному увеличению размера бандла. Если ProvidePlugin настроен на десятки глобальных переменных, Webpack будет вставлять соответствующие импорты во все файлы проекта, независимо от того, используются ли эти переменные в каждом конкретном файле. Это создаёт избыточный код, особенно в больших проектах с тысячами модулей.
Для диагностики проблем с shimming используйте webpack-bundle-analyzer — инструмент визуализации состава бандла. Если jQuery или другая библиотека появляется в бандле несколько раз, вероятно, конфликтуют разные версии или ProvidePlugin настроен на несколько идентификаторов, ведущих к разным версиям пакета. Решение — унифицировать версии зависимостей через resolve.alias и проверить, что все шимованные идентификаторы указывают на один и тот же модуль.
Прежде чем применять shimming, оцените возможность обновления библиотеки до версии, поддерживающей модульную систему. Многие legacy-пакеты имеют современные альтернативы, которые не требуют шимования. Например, jQuery-плагины можно заменить на нативные браузерные API: $.ajax → fetch, $.each → Array.forEach. Рефакторинг даёт долгосрочный выигрыш в поддержке, тогда как shimming — временное решение, которое усложняет конфигурацию.
Если обновление невозможно, рассмотрите NormalModuleReplacementPlugin, который позволяет подменять один модуль другим на уровне резолвинга, без изменения исходного кода. Этот плагин работает на этапе построения графа зависимостей, до применения лоадеров, и обрабатывает все обращения к модулю независимо от контекста. Это более чистое решение для замены целых библиотек, чем точечные лоадеры.
С развитием нативных ES модулей в браузерах и появлением import maps некоторые сценарии shimming могут быть решены без Webpack. Import maps позволяют переназначать имена модулей на лету на уровне браузера, без этапа сборки. Однако этот подход не поддерживается в React Native и других средах без браузерного ESM, поэтому shimming через Webpack остаётся актуальным для production-сборок, где требуется полный контроль над зависимостями и их версиями. Выбор между import maps и Webpack-шимами зависит от целевой платформы и требований к совместимости со старыми браузерами.
Часто задаваемые вопросы
Shimming добавляет код для обеспечения совместимости, а tree shaking удаляет неиспользуемый код. Эти техники противоположны по цели: shimming увеличивает размер бандла, tree shaking уменьшает. В production-сборке обе применяются последовательно.
Да, shimming существует как техника независимо от Webpack — например, через глобальные скрипты в HTML или через ES модули с реэкспортом. Однако Webpack предоставляет наиболее удобные инструменты автоматизации: ProvidePlugin и лоадеры, которые не требуют ручного изменения кода.
ProvidePlugin не влияет на скорость сборки, так как работает на этапе компиляции AST. imports-loader и exports-loader добавляют небольшое время обработки каждого файла. При использовании на сотнях файлов разница может составить 5–15% времени полной сборки.
Если все зависимости поддерживают ES модули и модульную систему, shimming избыточен. Отказ от shimming упрощает конфигурацию, уменьшает размер бандла и снижает риск конфликтов имён. Рекомендуется проверять зависимости на caniuse.com.
TypeScript требует дополнительных объявлений типов для шимованных переменных. Необходимо добавить declare const $: any или установить типы через @types/jquery. ProvidePlugin подставляет импорты на уровне JavaScript после компиляции TypeScript, поэтому типы проверяются отдельно.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также