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. Тези loaders работят на ниво отделни файлове, а не глобално като ProvidePlugin.
// webpack.config.js — настройка на imports-loader
module.exports = {
module: {
rules: [
{
test: /legacy-module\.js$/,
use: [
{
loader: 'imports-loader',
options: {
imports: [
'jquery',
'$',
],
},
},
],
},
],
},
};
exports-loader се използва, когато библиотеката присвоява стойност на глобална променлива, но не я експортира чрез модулната система. Loader-ът извлича стойността и я превръща в модулен експорт, което позволява на други модули да я импортират чрез import.
Shimming се конфигурира в webpack.config.js чрез комбинация от плъгини и loaders. Типичен сценарий включва 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, който позволява заместване на един модул с друг на нивото на разрешаване, без промяна на изходния код. Този плъгин работи на етапа на изграждане на графа на зависимостите, преди прилагането на loaders, и обработва всички обръщения към модула независимо от контекста. Това е по-чисто решение за замяна на цели библиотеки, отколкото целевите loaders.
С развитието на вградените 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 и loaders, които не изискват ръчна промяна на кода.
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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също