بالإضافة إلى ذلك، لا يمكن إعادة إنتاج التقطّع إلا على بعض المواقع. جعلتُ الـ LLM يقرأ كود الإضافة قليلًا، وبعد قليل سأطلب من خبير أن يلقي نظرة ليرى إن كانت لديه أفكار للتحسين.
من منظور معمارية تطوير الإضافة ومستوى التنفيذ، وبخصوص عنق الزجاجة في الأداء الناتج عن ترجمة الـ LLM، يمكن إجراء تحسينات عميقة عبر الأبعاد الأساسية التالية:
- إدخال معمارية معالجة غير متزامنة (Web Workers)
حاليًا قد تكون كمية كبيرة من مطابقة regex، وتحليل HTML، ومقارنة نصوص الترجمة تُنفَّذ داخل Content Script (الخيط الرئيسي).
- خطة التحسين: نقل المنطق الذي لا يتضمن عمليات DOM مباشرة (مثل تحليل بروتوكول البثّ التدريجي للـ LLM، والمعالجة المسبقة للنص، وخوارزميات المقارنة متعددة اللغات) إلى Web Worker
. - الأثر: تحرير الخيط الرئيسي، لضمان أن المتصفح أثناء معالجة بيانات الترجمة يظل قادرًا على الاستجابة بــ 60fps لتمرير الفأرة والنقر.
- تطبيق “المعالجة الدُفعية للرسم” و“تقسيم الوقت” (Scheduling)
أكثر ما يضرّ بالبثّ التدريجي (Streaming) هو تحديث DOM عند وصول كل حرف.
- خطة التحسين:
- تحديثات مُخزَّنة (Buffering): إعداد مخزن مؤقت 100ms-200ms، لدمج عدة مقاطع تُرجعها الـ LLM، ثم عبر
requestAnimationFrame إطلاق تحديث DOM واحد. - تقسيم الوقت (Time Slicing): إذا كان يلزم إدراج عدد كبير من عُقد الترجمة دفعة واحدة، فاستخدم أفكارًا شبيهة بـ React Fiber
لتجزئة المهمة إلى مهام صغيرة متعددة، وإدراجها واحدة تلو الأخرى عندما يكون المتصفح في حالة خمول (requestIdleCallback).
- تحديثات مُخزَّنة (Buffering): إعداد مخزن مؤقت 100ms-200ms، لدمج عدة مقاطع تُرجعها الـ LLM، ثم عبر
- الأثر: تحويل إعادة التدفق (Reflow) الكثيفة أصلًا إلى تحديثات إيقاعية، ما يخفض بشكل كبير الحمل اللحظي على CPU.
- “الرسم الكسول” المعتمد على منفذ العرض (Intersection Observer)
إذا كانت الصفحة طويلة، فإن الترجمة الكاملة ستؤدي إلى تحديث آلاف عُقد DOM في الخلفية في الوقت نفسه، حتى لو لم تكن هذه العُقد على الشاشة.
- خطة التحسين: استخدام واجهة IntersectionObserver API.
- لا تُطلق طلب الترجمة ولا تُثبّت DOM الخاص بالترجمة إلا عندما تدخل عُقد النص الأصلي إلى منفذ العرض (أو توشك على الدخول).
- بالنسبة لعُقد الترجمة البعيدة عن منفذ العرض، يمكن التفكير في تدميرها مؤقتًا أو إخفائها لتخفيف ضغط حساب التخطيط على المتصفح.
- الأثر: تقليل حجم الحسابات من “بحجم الصفحة كاملة” إلى “بحجم الشاشة”.
- تحسين إستراتيجية تثبيت DOM (Containment)
عند إدراج عُقد الترجمة، إن أفسدت التخطيط الأصلي فقد يؤدي ذلك إلى إعادة تدفق للصفحة بالكامل.
- خطة التحسين:
- CSS Containment: إضافة contain: layout; أو contain:
content; إلى حاوية ما بعد الترجمة. سيخبر هذا المتصفح بأن التغييرات داخل هذه العقدة لن تؤثر على التخطيط الخارجي. - عنصر نائب ثابت: قبل بدء الترجمة، قدّر ارتفاعًا كعنصر نائب وفق طول النص الأصلي، لتجنّب “اهتزاز” الصفحة المتكرر عند تدفّق نص الترجمة.
- CSS Containment: إضافة contain: layout; أو contain:
- الأثر: حصر نطاق إعادة التدفق ومنع “تحريك كل شيء بسبب تغيير واحد”.
- تقليل التشغيل المفرط لـ MutationObserver
لأن الإضافة نفسها تعدّل DOM، فهذا سيؤدي إلى تشغيل MutationObserver الخاص بها مرة أخرى.
- خطة التحسين:
- عند إدراج عُقد الترجمة بواسطة الإضافة، استخدم علامة عالمية (Flag) لحجب الاستماع مؤقتًا، أو داخل المستمع رشّح العُقد التي أنشأتها الإضافة نفسها عبر node.isTrusted
أو سمات محددة. - رفع العتبة الديناميكية لـ mutationChangeDelay: عند اكتشاف أن المستخدم يمرّر بسرعة عالية، زد التأخير تلقائيًا أو أوقف المسح.
- عند إدراج عُقد الترجمة بواسطة الإضافة، استخدم علامة عالمية (Flag) لحجب الاستماع مؤقتًا، أو داخل المستمع رشّح العُقد التي أنشأتها الإضافة نفسها عبر node.isTrusted
- “التحديث التدريجي” للبثّ بدلًا من “الاستبدال الكلي”
- خطة التحسين: إذا كانت عقدة الترجمة عبارة عن حاوية، فعند التحديث التدريجي يجب فقط إلحاق عُقد نصية، بدلًا من تنفيذ innerHTML = …
وإعادة بناء البنية الداخلية كل مرة. - الأثر: تقليل ضغط جمع القمامة (GC) وكلفة تحليل شجرة DOM.
خلاصة
مسار التحسين الأكثر فاعلية هو: معالجة البيانات عبر Web Worker → الرسم المُخزَّن عبر requestAnimationFrame → استخدام IntersectionObserver
لتقييد النطاق → عزل التخطيط عبر CSS contain. بهذه الطريقة يمكن فصل “الحمل الثقيل” للـ LLM عن “سلاسة” المتصفح فصلًا مثاليًا.
