نظرة من الداخل على متصفح الويب الحديث (الجزء 4)

Mariko Kosaka

إتاحة إدخال البيانات في أداة "المركّب"

هذه هي المقالة الأخيرة من سلسلة المقالات الأربعة التي تتناول متصفّح Chrome وتبحث في كيفية تعامله مع الرمز البرمجي لعرض موقع إلكتروني. في المشاركة السابقة، اطّلعنا على عملية التقديم وتعرّفنا على أداة الدمج. في هذه المشاركة، سنطّلع على كيفية إتاحة أداة "المركّب" للتفاعل السلس عند تلقّي إدخال المستخدم.

أحداث الإدخال من وجهة نظر المتصفّح

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

عندما تحدث إيماءة المستخدم مثل اللمس على الشاشة، فإن عملية المتصفح هي تلك التي تتلقى الإيماءة في البداية. ومع ذلك، لا تعرف عملية المتصفّح سوى مكان حدوث هذه الإيماءة لأنّه تتم معالجة المحتوى داخل علامة التبويب من خلال عملية العارض. لذلك، تُرسِل عملية المتصفّح نوع الحدث (مثل touchstart) وإحداثياته إلى عملية عرض الصفحة. تعالج عملية العارض الحدث بشكلٍ مناسب من خلال العثور على هدف الحدث وتشغيل أدوات معالجة الأحداث المرتبطة.

حدث إدخال
الشكل 1: حدث الإدخال الذي يتم توجيهه من خلال عملية المتصفّح إلى عملية عرض المحتوى

يتلقّى أداة الدمج أحداث الإدخال.

الشكل 2: إطار العرض الذي يحوم فوق طبقات الصفحة

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

فهم المنطقة غير القابلة للتمرير السريع

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

منطقة محدودة لا يمكن التمرير فيها سريعًا
الشكل 3: مخطّط بياني للإدخال الموضّح في المنطقة التي لا يمكن التمرير فيها سريعًا

الانتباه عند كتابة معالِجات الأحداث

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

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault();
    }
});

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

منطقة يمكن التمرير فيها ببطء على الصفحة بأكملها
الشكل 4: رسم بياني للإدخال الذي تم وصفه للمنطقة القابلة للتمرير السريع التي تغطي صفحة بأكملها

لتجنّب حدوث ذلك، يمكنك تمرير خيارات passive: true في معالج الأحداث. يشير ذلك إلى المتصفّح بأنّك لا تزال تريد الاستماع إلى الحدث في سلسلة التعليمات الرئيسية، ولكن يمكن لبرنامج تركيب الصور المتابعة وإنشاء إطار جديد أيضًا.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

التحقّق مما إذا كان الحدث قابلاً للإلغاء

الانتقال إلى أسفل الصفحة
الشكل 5: صفحة ويب مع جزء من الصفحة ثابت للتمرير الأفقي

تخيل أنّ لديك مربعًا في إحدى الصفحات تريد قصر اتجاه التمرير على التمرير الأفقي فقط.

يعني استخدام خيار passive: true في حدث المؤشر أنّ التمرير في الصفحة يمكن أن يكون سلسًا، ولكن ربما بدأ التمرير العمودي في الوقت الذي تريد فيه ضبط preventDefault للحدّ من اتجاه التمرير. يمكنك التحقّق من ذلك باستخدام طريقة event.cancelable.

document.body.addEventListener('pointermove', event => {
    if (event.cancelable) {
        event.preventDefault(); // block the native scroll
        /*
        *  do what you want the application to do here
        */
    }
}, {passive: true});

بدلاً من ذلك، يمكنك استخدام قاعدة CSS مثل touch-action لإزالة معالِج الأحداث بالكامل.

#area {
  touch-action: pan-x;
}

العثور على هدف الحدث

اختبار النتائج
الشكل 6: سلسلة المحادثات الرئيسية التي تبحث في سجلات الطلاء عن ما تم رسمه على نقطة x.y

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

تقليل عمليات إرسال الأحداث إلى سلسلة المهام الرئيسية

في المشاركة السابقة، ناقشنا كيف تُجدِّد شاشاتنا العادية الشاشة 60 مرة في الثانية، و كيفية مواكبة الإيقاع لعرض الصور المتحركة بسلاسة. بالنسبة إلى الإدخال، يرسل جهاز الشاشة التي تعمل باللمس المعتاد حدث اللمس من 60 إلى 120 مرة في الثانية، ويرسل الماوس المعتاد الأحداث 100 مرة في الثانية. حدث الإدخال له دقة أعلى من سرعة تحديث الشاشة.

إذا تم إرسال حدث مستمر مثل touchmove إلى سلسلة المهام الرئيسية 120 مرة في الثانية، قد يؤدي ذلك إلى تشغيل عدد كبير جدًا من اختبارات النتائج وتنفيذ JavaScript مقارنةً ببطء إعادة التحديث في الشاشة.

الأحداث غير المفلتَرة
الشكل 7: الأحداث التي تغمر المخطط الزمني للإطار وتتسبّب في حدوث تقطُّع في الصفحة

للحدّ من المكالمات المفرطة إلى سلسلة المحادثات الرئيسية، يدمج Chrome الأحداث المستمرة (مثل wheel وmousewheel وmousemove وpointermove وtouchmove) ويؤخّر إرسال الرسائل حتى قبل requestAnimationFrame التالي مباشرةً.

الأحداث المجمّعة
الشكل 8: المخطط الزمني نفسه كما في السابق، ولكن يتم دمج الحدث وتأخيره

يتم إرسال أي أحداث منفصلة مثل keydown وkeyup وmouseup وmousedown وtouchstart وtouchend على الفور.

استخدام "getCoalescedEvents" للحصول على أحداث داخل الإطار

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

getCoalescedEvents
الشكل 9: مسار إيماءة اللمس السلس على اليسار، ومسار محدود مدمج على اليمين
window.addEventListener('pointermove', event => {
    const events = event.getCoalescedEvents();
    for (let event of events) {
        const x = event.pageX;
        const y = event.pageY;
        // draw a line using x and y coordinates.
    }
});

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

في هذه السلسلة، اطّلعنا على آلية عمل متصفّح الويب. إذا لم يسبق لك التفكير في سبب اقتراح DevTools إضافة {passive: true} إلى معالِج الأحداث أو سبب كتابة سمة async في علامة النص البرمجي، نأمل أن تُلقي هذه السلسلة من المقالات بعض الضوء على سبب احتياج المتصفّح إلى هذه المعلومات لتقديم تجربة أسرع وأكثر سلاسة على الويب.

استخدام Lighthouse

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

التعرّف على كيفية قياس الأداء

قد تختلف التعديلات التي تُجريها لتحسين الأداء من موقع إلكتروني إلى آخر، لذا من المهم قياس أداء موقعك الإلكتروني وتحديد ما يناسبه على أفضل وجه. يقدّم فريق أدوات مطوّري البرامج في Chrome بعض الأدلة التعليمية حول كيفية قياس أداء موقعك الإلكتروني.

إضافة "سياسة الميزات" إلى موقعك الإلكتروني

إذا أردت اتّخاذ خطوة إضافية، يمكنك الاطّلاع على سياسة الميزات، وهي ميزة جديدة لمنصة الويب يمكن أن تكون بمثابة قاعدة توجيهية لك عند إنشاء مشروعك. يضمن تفعيل سياسة الميزات سلوكًا معيّنًا لتطبيقك ويمنعك من ارتكاب الأخطاء. على سبيل المثال، إذا كنت تريد التأكّد من أنّ تطبيقك لن يحظر التحليل مطلقًا، يمكنك تشغيل تطبيقك على سياسة النصوص البرمجية غير المتزامنة. عند تفعيل sync-script: 'none'، سيتم منع تنفيذ JavaScript الذي يحظر المحلل اللغوي. حيث يمنع ذلك أي رمز من التعليمات البرمجية من حظر المحلل اللغوي، ولا داعي لأن يقلق المتصفح بشأن إيقاف المحلل اللغوي مؤقتًا.

الخاتمة

شكرًا

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

نشكر بشدة جميع من راجعوا المسودات الأولى من هذه السلسلة، بما في ذلك (على سبيل المثال لا الحصر): أليكس راسل، بول إيريش، ميغين كيرن، إريك بيدلمان، ماتياس بينينز، أدي عثماني، كينوكو ياسودا، ناسكو أوسكوف، وشارلي ريس.

هل أعجبتك هذه السلسلة؟ إذا كانت لديك أي أسئلة أو اقتراحات بشأن المشاركة المستقبلية، يسرّني التواصل معك في قسم التعليقات أدناه أو من خلال إرسال رسالة @kosamari على Twitter.