Shimming هو أسلوب لضمان توافق الوحدات التي تتوقع متغيرات عامة أو واجهات برمجية معينة. في بيئة 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 يجعل الكود الموجود متوافقًا مع البيئة التي يعمل فيها. الاختيار بينهما يعتمد على المشكلة المطلوب حلها: غياب واجهة برمجية أو عدم توافق الواجهات.
Webpack يعامل كل وحدة ككيان معزول له نطاق رؤيته الخاص. إذا كانت المكتبة تصل إلى المتغير العام jQuery عبر window.$، فسينتهي البناء بخطأ لأن هذا المتغير غير موجود في السياق النمطي. ProvidePlugin يحل المشكلة في مرحلة التجميع: عند اكتشاف المعرّف $ في الكود، يدرج الإضافة تلقائيًا import $ from 'jquery' في بداية الملف.
// الكود المصدري (الوحدة القديمة تصل إلى jQuery العامة)
$('.element').hide();
// بعد معالجة ProvidePlugin (يقوم Webpack بإدراج الاستيراد)
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 لاستبعاد الوحدات المكررة.
خطأ شائع آخر هو محاولة تطبيق shimming على وحدات تستخدم استدعاءات require المتزامنة من CommonJS في سياق ديناميكي. يعالج ProvidePlugin المعرّفات الثابتة فقط، لذلك يجب استبدال الإشارات الديناميكية يدويًا أو باستخدام NormalModuleReplacementPlugin.
قد يؤدي الإعداد غير الصحيح لـ shimming إلى زيادة كبيرة في حجم الحزمة. إذا كان ProvidePlugin مضبوطًا على عشرات المتغيرات العامة، فسيُدرج Webpack الاستيرادات المقابلة في جميع ملفات المشروع، بغض النظر عن استخدام هذه المتغيرات في كل ملف معين. هذا يخلق كودًا زائدًا، خاصة في المشاريع الكبيرة التي تحتوي على آلاف الوحدات.
لتشخيص مشاكل shimming استخدم webpack-bundle-analyzer — أداة لتصور مكونات الحزمة. إذا ظهرت jQuery أو مكتبة أخرى في الحزمة عدة مرات، فمن المحتمل أن إصدارات مختلفة تتعارض أو أن ProvidePlugin مضبوط على عدة معرّفات تؤدي إلى إصدارات مختلفة من الحزمة. الحل هو توحيد إصدارات التبعيات عبر resolve.alias والتأكد من أن جميع المعرّفات المعالجة تشير إلى نفس الوحدة.
قبل تطبيق shimming، قيّم إمكانية تحديث المكتبة إلى إصدار يدعم النظام النمطي. لدى العديد من الحزم القديمة بدائل حديثة لا تتطلب shimming. على سبيل المثال، يمكن استبدال إضافات jQuery بواجهات المتصفح الأصلية: $.ajax ← fetch، $.each ← Array.forEach. توفر إعادة البنية مكسبًا طويل الأمد في الصيانة، بينما shimming حل مؤقت يعقّد الإعداد.
إذا لم يكن التحديث ممكنًا، ففكر في NormalModuleReplacementPlugin الذي يسمح باستبدال وحدة بأخرى على مستوى التحليل دون تغيير الكود المصدري. تعمل هذه الإضافة في مرحلة بناء مخطط التبعيات، قبل تطبيق اللوادر، وتعالج جميع الإشارات إلى الوحدة بغض النظر عن السياق. هذا حل أنظف لاستبدال مكتبات كاملة من اللوادر النقطية.
مع تطور الوحدات ES الأصلية في المتصفحات وظهور import maps، يمكن حل بعض سيناريوهات shimming دون Webpack. تتيح import maps إعادة تعيين أسماء الوحدات أثناء التشغيل على مستوى المتصفح، دون مرحلة بناء. ومع ذلك، هذا النهج غير مدعوم في React Native والبيئات الأخرى الخالية من ESM المتصفحي، لذلك يبقى shimming عبر Webpack مهمًا لعمليات البناء الإنتاجية التي تتطلب تحكمًا كاملًا في التبعيات وإصداراتها. يعتمد الاختيار بين import maps وWebpack shims على المنصة المستهدفة ومتطلبات التوافق مع المتصفحات القديمة.
الأسئلة الشائعة
Shimming يضيف كودًا لضمان التوافق، بينما tree shaking يزيل الكود غير المستخدم. هذان الأسلوبان متعاكسان في الهدف: shimming يزيد حجم الحزمة، وtree shaking يقلله. في البناء الإنتاجي يُطبقان بالتتابع.
نعم، 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. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا