دراسة حالة · مفتوح المصدر

react-inp-chunker

مكتبة React وTypeScript مفتوحة المصدر وبلا واجهة مفروضة تمنع العمل المتزامن الثقيل من حجب تفاعل المستخدم. تعالج العمل في شرائح زمنية (time slices) من بضع أجزاء من الثانية وتعيد التحكم إلى الخيط الرئيسي بينها، فلا تضر التحديثات الطويلة بمؤشر Interaction to Next Paint (INP).

npm install react-inp-chunker

إثبات المشروع

دوري

  • صممت الواجهة البرمجية ومحرك الجدولة ونموذج الحالات، وكتبت المكتبة بلغة TypeScript الصارمة.
  • كتبت التوثيق والبيئة التجريبية الحيّة التي تشغّل العمل نفسه مع التقسيم ودونه.
  • أنشر الحزمة على npm وأتولى صيانتها بترخيص MIT.

القيود

  • لم يكن بوسع المكتبة أن تجعل العمل أسرع، بل أن تضمن ألا يشغل الخيط الرئيسي أكثر من بضع أجزاء من الثانية في كل مرة.
  • كان عليها أن تبقى بلا واجهة مفروضة وأن تعمل مع React 18 و19 ومع العرض على الخادم وخارج React.
  • كان على النتائج أن تبقى متسقة في React المتزامن، دون أن يعمل شيء بعد الإلغاء أو إزالة المكون.

القرارات الرئيسية

  • فصلت نواة جدولة لا تعتمد على React عن الخطافات ليعمل المحرك نفسه مع TypeScript أو داخل Web Worker أو في Node.
  • أقرأ الساعة بعد كل عنصر لأحصر تجاوز كل شريحة في عنصر واحد، وأضفت batchSize للعمل الأرخص من أن يُقاس عنصرًا عنصرًا.
  • اخترت آلية إعادة التحكم بحسب المتصفح: scheduler.yield() ثم MessageChannel ثم setTimeout.
نوع المشروع
مكتبة React مفتوحة المصدر على npm
الدور
المؤلف والمشرف على الصيانة
التركيز الأساسي
مؤشر INP وجدولة الخيط الرئيسي وتجربة المطور
النطاق التقني
TypeScript وReact 18+ وScheduler API
  • مفتوح المصدر
  • React
  • TypeScript
  • INP
  • Core Web Vitals
  • حزمة npm

السياق

يشغّل خيط واحد شيفرة JavaScript في الصفحة، ويعالج كل نقرة وكل ضغطة مفتاح، ويرسم الشاشة. تصفية آلاف الصفوف أو تحليل ملف CSV كبير أو بناء فهرس بحث داخل مكون React يشغل ذلك الخيط، وأي تفاعل يصل في أثناء ذلك ينتظر في طابور.

المهمة التي تتجاوز 50 ملي ثانية مهمة طويلة، والتفاعل الذي يقع في أثنائها هو أكثر الأسباب شيوعًا للإخفاق في مؤشر Interaction to Next Paint (INP)، وهو مقياس Core Web Vitals لسرعة الاستجابة.

المشكلة

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

  • تحجب الحلقة المتزامنة الطويلة النقرات والكتابة والرسم حتى تنتهي.
  • إعادة التحكم يدويًا باستدعاءات setTimeout المتداخلة مقيدة بتأخير أدنى يقارب 4 ملي ثانية، وتوزع منطق الجدولة على المكونات.
  • يجب أن يصل التقدم والإيقاف المؤقت والاستئناف والإلغاء إلى حالة React دون التمزّق البصري (Tearing)، أي ظهور قيم غير متّسقة في الواجهة أثناء العرض المتزامن.
  • لا يجوز أن يصل أي تحديث للحالة بعد إزالة المكون.

النهج

تعالج المكتبة العناصر واحدًا تلو الآخر حتى تنفد الشريحة الزمنية (5 ملي ثانية افتراضيًا)، أو حتى يبلغ المتصفح عن تفاعل معلق. عندها تعيد التحكم ليعالج المتصفح الإدخال ويرسم، ثم تستأنف من حيث توقفت.

تعيد التحكم بأفضل أداة يوفرها المتصفح: scheduler.yield() حيث يكون مدعومًا، وMessageChannel في Safari والمتصفحات الأقدم، وsetTimeout(0) كحل أخير.

  • يطبق useChunkedTask دالة واحدة على قائمة، ويعيد العرض مع التقدم مرة واحدة لكل شريحة لا لكل عنصر.
  • يشغّل useDeferredTask عملًا مكتوبًا كمولّد، ويتوقف عند كل yield عندما تنفد الشريحة.
  • توفر react-inp-chunker/core الدالتين createChunkScheduler وcreateDeferredTask دون استيراد React، لاستخدامهما مع TypeScript فقط أو داخل Web Worker أو في سكربت Node.
  • يوفر المحركان start وpause وresume وcancel، والزوج subscribe وgetSnapshot، ووعد settled يُحسم دائمًا.
  • يضبط الخياران batchSize وcollectResults الحالات القصوى، ولا يحتاج معظم الشيفرة إلى أي منهما.

الضمانات وتنبيهات التطوير

لا تتجاوز الشريحة حدها بأكثر من عنصر واحد، أو بعدد batchSize من العناصر إذا رُفع هذا الخيار، لأن الساعة تُقرأ بعد كل عنصر. ولا تبلغ المهمة المكتملة عن نتائجها متأخرة بعرض واحد، ولا يعمل شيء بعد cancel()، ولا يصل أي تحديث للحالة بعد إزالة المكون.

في بيئة التطوير، يُبلغ عن أي عنصر واحد يحجب الخيط أكثر من 50 ملي ثانية مع رقمه، ويحصل العمل الرخيص الذي تطغى عليه قراءة الساعة على اقتراح لقيمة batchSize. وتُزال التنبيهات كلها من نسخة الإنتاج التي تختارها أدوات الحزم عبر شرط التصدير production.

التوافق والجودة والحجم

تدعم الحزمة React 18 و19، وNode 18 و20 و22 و24، وESM وCommonJS، والعرض على الخادم حيث يعرض الخطافان حالة خاملة ولا يشغّلان شيئًا. وتعمل مجموعة الاختبارات على الإصدارين الرئيسيين من React بتغطية 100% للعبارات والفروع والدوال والأسطر.

لا تعتمد الحزمة على شيء غير React. ويذكر README حجم 1.83 كيلوبايت لـ useChunkedTask و1.72 كيلوبايت لـ useDeferredTask و1.34 كيلوبايت لـ createChunkScheduler دون React و2.35 كيلوبايت للحزمة كاملة بعد التصغير والضغط بـ Brotli. وتُنشر الإصدارات من CI عبر npm Trusted Publishing مع إثبات مصدر موقّع.

النتائج

  • منشورة على npm باسم react-inp-chunker بترخيص MIT، مع توثيق وبيئة تجريبية حيّة على GitHub Pages.
  • توفر خطافين لـ React هما useChunkedTask وuseDeferredTask، ونواة لا تعتمد على React تشغّل محرك الجدولة نفسه.
  • تُبقي كل شريحة عمل في حدود بضع أجزاء من الثانية افتراضيًا، فيمكن معالجة الإدخال والرسم بين الشرائح.
  • تتحقق في كل دفع من ادعاءات التوافق، ومنها React 18 و19 وأربعة إصدارات من Node وESM وCommonJS.

الخطوات التالية

  • اقرأ التوثيق وجرّب البيئة التجريبية الحيّة.
  • ثبّتها بالأمر npm install react-inp-chunker.
  • أبلغ عن المشكلات أو ساهم على GitHub.