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

ماريكو كوساكا

سيتم إرسال الإدخال إلى Compositor.

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

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

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

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

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

يتلقى المكون أحداث الإدخال

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

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

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

ولأنّ تشغيل 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 مرة في الثانية وكيف نحتاج إلى مواكبة وتيرة إنشاء رسوم متحركة سلسة. وللإدخال، يسلم الجهاز النموذجي المزوّد بشاشات لمس يكون حدث الإدخال هذا بدقة أعلى ممّا يمكن إعادة تحميل الشاشة به.

إذا تم إرسال حدث مستمر، مثل 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.
    }
});

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

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

استخدام Lighthouse

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

مزيد من المعلومات عن كيفية قياس الأداء

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

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

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

الخاتمة

شكرًا

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

شكرًا جزيلاً لكل من راجع المسودات الأولية لهذه السلسلة، بما في ذلك (على سبيل المثال لا الحصر): أليكس راسل، بول أيرش، ميجين كيرني، إريك بيدلمان، ماتياس بينينس، آدي عثماني، Kinuko Russell، بول أيرش، ميجين كيرني، إريك بيدلمان، ماتياس بينينس، آدي عثماني، Kinuko Russell، Kinuko Russell، Kinuko3 Yasuda، {1Kinuko Yasuda

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