Shimming: جوهره وأساليبه ومبدأ عمله

المؤلف: IT Sectr نُشر: 2026-05-19 وقت القراءة: 8 دق

Shimming هو أسلوب لضمان توافق الوحدات التي تتوقع متغيرات عامة أو واجهات برمجية معينة. في بيئة Webpack، يتم تطبيق shimming عبر ProvidePlugin وimports-loader وexports-loader، مما يسمح بربط المكتبات القديمة (legacy) دون تغيير كودها المصدري. وفقًا لـ Webpack Documentation (2026)، لا يزال shimming أداة أساسية لدمج إضافات jQuery وغيرها من التبعيات التي لا تدعم النظام النمطي.

الأهم

  • Shimming هو أسلوب لاستبدال المتغيرات العامة وواجهات البرمجة لضمان توافق الوحدات في البناء.
  • ProvidePlugin يستورد الوحدة تلقائيًا عند اكتشاف إشارة إلى متغير عام في الكود.
  • imports-loader وexports-loader يتحكمان في نطاق رؤية الوحدات، بإضافة أو تعديل واجهاتها.
  • Shim يختلف عن polyfill في أنه لا ينفّذ وظائف مفقودة، بل يعيد توجيه الاستدعاءات الموجودة.
  • Webpack يوفر آليات shimming مدمجة دون الحاجة إلى تثبيت حزم إضافية.

ما هو Shimming؟

Shimming هو أسلوب برمجي يدمج طبقة توافق بين الكود والبيئة دون تغيير الكود المصدري للوحدة. في سياق بناء JavaScript، يحل shimming المشكلة عندما تصل الوحدة إلى متغيرات عامة (window.$, global.process) غير موجودة في البيئة النمطية.

Shim وpolyfill: الاختلافات الرئيسية

Polyfill ينفّذ الوظائف المفقودة من الصفر، مضيفًا إمكانيات جديدة إلى البيئة. على سبيل المثال، core-js يضيف Array.prototype.flatMap للمتصفحات القديمة. أما Shim فيعيد توجيه الاستدعاءات الموجودة إلى تطبيقات متاحة أو يستبدل الكائنات العامة المتوقعة. في Webpack، يقوم ProvidePlugin تلقائيًا بإدراج import $ from 'jquery' في كل مكان تظهر فيه إشارة إلى المتغير العام $، دون الحاجة إلى تعديلات في الكود.

الاختلاف الأساسي يكمن في الهدف. Polyfill يضيف ما لا يوجد، بينما shim يجعل الكود الموجود متوافقًا مع البيئة التي يعمل فيها. الاختيار بينهما يعتمد على المشكلة المطلوب حلها: غياب واجهة برمجية أو عدم توافق الواجهات.

كيف يعمل Shimming في Webpack

Webpack يعامل كل وحدة ككيان معزول له نطاق رؤيته الخاص. إذا كانت المكتبة تصل إلى المتغير العام jQuery عبر window.$، فسينتهي البناء بخطأ لأن هذا المتغير غير موجود في السياق النمطي. ProvidePlugin يحل المشكلة في مرحلة التجميع: عند اكتشاف المعرّف $ في الكود، يدرج الإضافة تلقائيًا import $ from 'jquery' في بداية الملف.

js
// الكود المصدري (الوحدة القديمة تصل إلى jQuery العامة)
$('.element').hide();

// بعد معالجة ProvidePlugin (يقوم Webpack بإدراج الاستيراد)
import $ from 'jquery';
$('.element').hide();

بالإضافة إلى ذلك، يسمح imports-loader بتحديد التبعيات التي يجب أن تستقبلها الوحدة بشكل صريح. هذا مفيد عندما تستخدم المكتبة this في المستوى الأعلى، متوقعة أن يشير this إلى window وليس إلى module.exports.

ProvidePlugin: متغيرات عامة للوحدات

ProvidePlugin هو إضافة مدمجة في Webpack تحمّل الوحدات تلقائيًا عند اكتشاف إشارات إلى معرّفات محددة. الإعداد عبارة عن كائن حيث المفتاح هو اسم المتغير والقيمة هي المسار إلى الوحدة والحقل المُصدَّر.

إعداد الإضافة

js
// 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

imports-loader يضيف الاستيرادات اللازمة في بداية الوحدة، بينما exports-loader يحدد القيم المُصدَّرة للوحدات التي لا تستخدم module.exports بشكل صريح. تعمل هذه اللوادر على مستوى ملفات منفردة، وليس بشكل عام مثل ProvidePlugin.

إصلاح التبعيات باستخدام imports-loader

js
// 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

Shimming يُكوَّن في webpack.config.js عبر مجموعة من الإضافات واللوادر. يتضمن السيناريو النموذجي ProvidePlugin للمتغيرات العامة وimports-loader للوحدات المحددة التي تتطلب تغيير نطاق الرؤية.

إعداد Webpack الأساسي للـ shimming

js
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

Shimming أداة قوية لكنها خطيرة. يؤدي الإعداد غير الصحيح إلى تكرار الكود في الحزمة وتضارب الأسماء وأخطاء تنفيذ غير متوقعة. ينسى المطورون غالبًا أن ProvidePlugin يعمل في مرحلة التجميع ولا يمكنه معالجة الإشارات الديناميكية إلى المتغيرات.

تضارب المتغيرات العامة

إذا استخدمت إضافتان إصدارين مختلفين من jQuery، فسيستبدل ProvidePlugin أحدهما فقط، وهو المحدد أولًا في الإعداد. ستحصل المكتبة الثانية على إصدار غير متوافق، مما يسبب أخطاء يصعب تتبعها. الحل هو استخدام exports-loader لكل مكتبة مع تحديد الإصدار صراحة أو تطبيق webpack.IgnorePlugin لاستبعاد الوحدات المكررة.

خطأ شائع آخر هو محاولة تطبيق shimming على وحدات تستخدم استدعاءات require المتزامنة من CommonJS في سياق ديناميكي. يعالج ProvidePlugin المعرّفات الثابتة فقط، لذلك يجب استبدال الإشارات الديناميكية يدويًا أو باستخدام NormalModuleReplacementPlugin.

مشاكل الأداء عند shimming غير صحيح

قد يؤدي الإعداد غير الصحيح لـ shimming إلى زيادة كبيرة في حجم الحزمة. إذا كان ProvidePlugin مضبوطًا على عشرات المتغيرات العامة، فسيُدرج Webpack الاستيرادات المقابلة في جميع ملفات المشروع، بغض النظر عن استخدام هذه المتغيرات في كل ملف معين. هذا يخلق كودًا زائدًا، خاصة في المشاريع الكبيرة التي تحتوي على آلاف الوحدات.

لتشخيص مشاكل shimming استخدم webpack-bundle-analyzer — أداة لتصور مكونات الحزمة. إذا ظهرت jQuery أو مكتبة أخرى في الحزمة عدة مرات، فمن المحتمل أن إصدارات مختلفة تتعارض أو أن ProvidePlugin مضبوط على عدة معرّفات تؤدي إلى إصدارات مختلفة من الحزمة. الحل هو توحيد إصدارات التبعيات عبر resolve.alias والتأكد من أن جميع المعرّفات المعالجة تشير إلى نفس الوحدة.

بدائل shimming: إعادة البنية وتحديث التبعيات

قبل تطبيق shimming، قيّم إمكانية تحديث المكتبة إلى إصدار يدعم النظام النمطي. لدى العديد من الحزم القديمة بدائل حديثة لا تتطلب shimming. على سبيل المثال، يمكن استبدال إضافات jQuery بواجهات المتصفح الأصلية: $.ajaxfetch، $.eachArray.forEach. توفر إعادة البنية مكسبًا طويل الأمد في الصيانة، بينما shimming حل مؤقت يعقّد الإعداد.

إذا لم يكن التحديث ممكنًا، ففكر في NormalModuleReplacementPlugin الذي يسمح باستبدال وحدة بأخرى على مستوى التحليل دون تغيير الكود المصدري. تعمل هذه الإضافة في مرحلة بناء مخطط التبعيات، قبل تطبيق اللوادر، وتعالج جميع الإشارات إلى الوحدة بغض النظر عن السياق. هذا حل أنظف لاستبدال مكتبات كاملة من اللوادر النقطية.

Shimming في JavaScript الحديثة: ESM وimport maps

مع تطور الوحدات ES الأصلية في المتصفحات وظهور import maps، يمكن حل بعض سيناريوهات shimming دون Webpack. تتيح import maps إعادة تعيين أسماء الوحدات أثناء التشغيل على مستوى المتصفح، دون مرحلة بناء. ومع ذلك، هذا النهج غير مدعوم في React Native والبيئات الأخرى الخالية من ESM المتصفحي، لذلك يبقى shimming عبر Webpack مهمًا لعمليات البناء الإنتاجية التي تتطلب تحكمًا كاملًا في التبعيات وإصداراتها. يعتمد الاختيار بين import maps وWebpack shims على المنصة المستهدفة ومتطلبات التوافق مع المتصفحات القديمة.

الأسئلة الشائعة

ما الفرق بين shimming وtree shaking؟

Shimming يضيف كودًا لضمان التوافق، بينما tree shaking يزيل الكود غير المستخدم. هذان الأسلوبان متعاكسان في الهدف: shimming يزيد حجم الحزمة، وtree shaking يقلله. في البناء الإنتاجي يُطبقان بالتتابع.

هل يمكن استخدام shimming بدون Webpack؟

نعم، shimming موجود كأسلوب مستقل عن Webpack — على سبيل المثال، عبر النصوص العامة في HTML أو عبر وحدات ES مع إعادة التصدير. لكن Webpack يوفر أدوات التشغيل الآلي الأكثر ملاءمة: ProvidePlugin واللوادر التي لا تتطلب تغييرًا يدويًا في الكود.

كيف يؤثر shimming على أداء البناء؟

ProvidePlugin لا يؤثر في سرعة البناء لأنه يعمل في مرحلة تجميع AST. يضيف imports-loader وexports-loader وقت معالجة صغيرًا لكل ملف. عند الاستخدام على مئات الملفات، قد يصل الفرق إلى 5–15% من وقت البناء الكلي.

متى يجب التخلي عن shimming؟

إذا كانت جميع التبعيات تدعم وحدات ES والنظام النمطي، فإن shimming يصبح زائدًا عن الحاجة. التخلي عن shimming يبسّط الإعداد ويقلل حجم الحزمة ويخفض خطر تضارب الأسماء. يُنصح بالتحقق من التبعيات على caniuse.com.

كيف يعمل shimming مع TypeScript؟

TypeScript يتطلب إعلانات أنواع إضافية للمتغيرات المعالجة. من الضروري إضافة declare const $: any أو تثبيت الأنواع عبر @types/jquery. يدرج ProvidePlugin الاستيرادات على مستوى JavaScript بعد تجميع TypeScript، لذلك تُفحص الأنواع بشكل منفصل.

الخلاصة

  • Shimming هو أسلوب لضمان توافق الوحدات مع البيئة عبر استبدال المتغيرات العامة وواجهات البرمجة.
  • ProvidePlugin يستورد الوحدات تلقائيًا عند اكتشاف إشارات إلى معرّفات محددة في الكود.
  • imports-loader يضيف استيرادات في بداية ملفات معينة، بينما exports-loader يحدد القيم المُصدَّرة.
  • Shim يختلف عن polyfill في أنه لا ينفّذ الوظائف، بل يعيد توجيه الاستدعاءات إلى تطبيقات موجودة.
  • ProvidePlugin يعمل في مرحلة التجميع ولا يعالج الإشارات الديناميكية إلى المتغيرات.
  • حقل globalObject في output يحدد السياق الصحيح للمستوى الأعلى في البيئة المستهدفة.
  • استخدم shimming فقط للوحدات التي لا تدعم النظام النمطي الحديث، وتخلَّ عنه عند الدعم الكامل لوحدات ES.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا