البحث الشامل عن RenderingNG: BlinkNG

ستيفان زاجر
ستيفان زاجر
كريس هارلسون
كريس هارلسون

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

Blink بدأ حياته كمجموعة WebKit، والتي هي في حد ذاتها شوكة من KHTML، والتي يعود تاريخها إلى 1998. ويشتمل على بعض أقدم الرموز (والأكثر أهمية) في Chromium، وبحلول عام 2014 كان يُظهر عمره بالتأكيد. في ذلك العام، شرعنا في مجموعة من المشاريع الطموحة تحت اسم ما نسميه BlinkNG، بهدف معالجة أوجه القصور طويلة الأمد في تنظيم وبنية رمز Blink. تستكشف هذه المقالة مشروع BlinkNG ومشاريعه المكونة: أسباب نجاحنا، وما أنجزه، والمبادئ التوجيهية التي شكّلت تصميمه، وفرص التحسين المستقبلية التي تحملها.

مسار العرض قبل BlinkNG وبعده.

عرض ما قبل NG

كان مسار العرض في Blink دائمًا مقسّمًا إلى مراحل (style وlayout وpaint وغير ذلك)، إلا أنّ حواجز التجريد كانت متسرّبة. بشكل عام، تكوّنت البيانات المرتبطة بالعرض من عناصر طويلة العمر وقابلة للتغيير. يمكن تعديل هذه العناصر وتعديلها في أي وقت وإعادة تدويرها وإعادة استخدامها بشكل متكرر من خلال إجراء تعديلات متتالية على العرض. لم يكن من الممكن الإجابة بشكل موثوق عن أسئلة بسيطة مثل:

  • هل يلزم تحديث ناتج النمط أو التخطيط أو الطلاء؟
  • متى ستحصل هذه البيانات على قيمتها "النهائية"؟
  • ما هو الوقت المناسب لتعديل هذه البيانات؟
  • متى سيتم حذف هذا الكائن؟

هناك العديد من الأمثلة على ذلك، بما في ذلك:

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

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

سينشئ النمط بُنى بيانات ملحقة تحدد مسار الإنشاء، ويتم تعديل بُنى البيانات هذه في كل مرحلة بعد النمط.

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

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

التغييرات التي أجريناها

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

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

ستشكل القائمة الكاملة لمشروعات BlinkNG الفرعية قراءة مملة، ولكن فيما يلي بعض النتائج الخاصة.

دورة حياة المستند

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

  • إذا كنا بصدد تعديل سمة ComputedStyle، يجب أن تكون دورة حياة المستند kInStyleRecalc.
  • إذا كانت حالة DocumentLifecycle هي kStyleClean أو أحدث، فيجب أن تعرض NeedsStyleRecalc() القيمة false لأي عقدة مرفقة.
  • عند الدخول في مرحلة دورة حياة طلاء، يجب أن تكون حالة دورة الحياة kPrePaintClean.

وأثناء تنفيذ BlinkNG، أزلنا بشكل منهجي مسارات الرمز البرمجي التي انتهكت هذه القيم الثابتة، وأضفنا العديد من التأكيدات الإضافية في جميع أنحاء الرمز لضمان عدم التراجع عن هذا الإجراء.

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

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

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

نمط الأنابيب والتخطيط والرسم المسبق

بشكل عام، تكون مراحل العرض قبل الطلاء مسؤولة عما يلي:

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

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

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

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

فيما يلي بعض المشروعات المهمة التي تخلصت من العيوب المعمارية في مراحل العرض قبل الطلاء.

Project Squad: تنظيم مرحلة الأسلوب

عالج هذا المشروع عيوبًا رئيسية في مرحلة النمط مما منعه من التنفيذ بشكل واضح:

هناك مخرجان أساسيان لمرحلة النمط: ComputedStyle يحتويان على نتيجة تشغيل خوارزمية تتالي CSS فوق شجرة DOM، وشجرة LayoutObjects التي تحدد ترتيب العمليات لمرحلة التنسيق. من الناحية النظرية، ينبغي أن يحدث تشغيل خوارزمية الشلال بشكل صارم قبل إنشاء شجرة التخطيط؛ ولكن في السابق، كانت هاتان العمليتان متداخلتين. نجحت فريق المشروع في تقسيم هاتين المرحلتين إلى مرحلتين مختلفتين ومتسلسلة.

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

LayoutNG: تنفيذ مرحلة التخطيط

كان هذا المشروع العملي الذي يشكّل أحد الركائز الأساسية في RenderingNG إعادة صياغة كاملة لمرحلة عرض التصميم. لن نحقق الانتصاف للمشروع بأكمله هنا، ولكن هناك بعض الجوانب البارزة لمشروع BlinkNG الشامل:

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

مرحلة ما قبل الرسم

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

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

أشجار المواقع: الأشكال الهندسية المتسقة

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

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

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

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

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

المركّب بعد الطلاء: الطلاء وتركيب الأنابيب

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

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

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

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

كانت خطتنا تهدف مع مرور الوقت إلى التخلص من كل عناصر مواقع استدعاء DisableCompositingQueryAssert، ثم الإعلان عن أنّ الرمز آمن وصحيح. لكن ما اكتشفناه هو أن عددًا من الطلبات كان من المستحيل في الأساس إزالة عدد ما دامت الطبقات قد حدثت قبل الطلاء. (لقد تمكّنا أخيرًا من إزالتها مؤخّرًا جدًا فقط!) كان هذا هو السبب الأول الذي تم اكتشافه لمشروع Composite After Paint. ما تعلمناه هو أنه، حتى لو كانت لديك مرحلة مسار محددة جيدًا لعملية ما، فستتعطل في النهاية إذا كانت في مكان خاطئ ضمن مسار العملية.

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

المزايا

كما لاحظت، يحقّق مسار العرض المحدَّد جيدًا فوائد هائلة على المدى الطويل. هناك أكثر مما قد تعتقد:

  • موثوقية محسّنة إلى حد كبير: هذه العملية واضحة جدًا. التعليمة البرمجية الأكثر وضوحًا ذات واجهات محددة ومفهومة جيدًا يسهل فهمها وكتابتها واختبارها. هذا يجعلها أكثر موثوقية. وهو أيضًا يجعل الرمز البرمجي أكثر أمانًا واستقرارًا، من خلال الحدّ من الأعطال وتقليل الأخطاء الناتجة عن الاستخدام بعد الاستخدام.
  • توسيع نطاق التغطية التجريبية: في إطار BlinkNG، أضفنا عددًا كبيرًا من الاختبارات الجديدة إلى مجموعتنا. يشمل ذلك اختبارات الوحدة التي توفّر التحقّق المركّز من العناصر الداخلية، واختبارات الانحدار التي تمنعنا من إعادة تقديم الأخطاء القديمة التي أصلحناها (الكثير من الأخطاء!)؛ والكثير من الإضافات المتاحة للجميع والتي تتم صيانتها بشكلٍ جماعي حزمة اختبار النظام الأساسي للويب، والتي تستخدمها جميع المتصفحات لقياس مدى التوافق مع معايير الويب.
  • سهولة التوسيع: في حال تقسيم النظام إلى مكونات واضحة، ليس من الضروري فهم مكوّنات أخرى بأي مستوى من التفاصيل لإحراز تقدّم في المكوّن الحالي. يسهّل ذلك على الجميع إضافة قيمة إلى رمز العرض بدون الحاجة إلى أن يكونوا خبراء متعمّقين، كما يسهِّل شرح سلوك النظام بأكمله.
  • الأداء: يُعدّ تحسين الخوارزميات المكتوبة برموز السباغيتي أمرًا صعبًا بما فيه الكفاية، إلا أنّه من المستحيل تقريبًا تحقيق أهداف أكبر مثل التمرير المتسلسل إلى سلاسل الرموز والرسوم المتحركة أو العمليات وسلاسل المحادثات لعزل المواقع الإلكترونية بدون هذا المسار. يمكن أن يساعدنا التوازي في تحسين الأداء بصورة كبيرة، ولكنه معقد للغاية.
  • تحقيق النتائج والاحتواء: هناك العديد من الميزات الجديدة التي وفّرتها BlinkNG والتي تمارس المسارات بطرق جديدة ومبتكرة. على سبيل المثال، ماذا لو أردنا تشغيل مسار العرض فقط إلى أن تنتهي صلاحية الميزانية؟ أو هل تريد تخطي العرض للأشجار الفرعية المعروفة بأنها غير ملائمة للمستخدم الآن؟ وهذا ما تُفعِّله خاصية CSS content- visibility. ماذا عن جعل نمط المكون يعتمد على تخطيطه؟ وهي طلبات بحث للحاويات.

دراسة حالة: طلبات البحث عن الحاوية

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

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

كيف يمكننا حل هذه المشكلة؟ أليس الأمر تبعية على مسار عكسي، أي، المشكلة نفسها التي يتم حلها في مشروعات مثل Composite After Paint؟ والأسوأ من ذلك، ماذا لو غيّرت الأنماط الجديدة حجم الأصل؟ ألن يؤدي ذلك أحيانًا إلى حدوث تكرار لا نهائي؟

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

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

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

المستقبل: تكوين خارج سلسلة التعليمات الرئيسية ... وما إلى ذلك!

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

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

يسرني إخبارك بأنّه ليس عليك أن تكون بهذه الطريقة. يعود هذا الجانب من بنية Chromium إلى أيام KHTML، عندما كان التنفيذ المترابط المترابط هو نموذج البرمجة السائد. وعندما أصبحت المعالِجات متعددة النواة شائعة في الأجهزة ذات مستوى المستهلك، تم دمج الافتراض ذي السلسلة الفردية بدقة في Blink (المعروف سابقًا باسم WebKit). أردنا إضافة المزيد من السلاسل في محرك العرض لفترة طويلة، ولكن كان ذلك ببساطة مستحيلاً في النظام القديم. وكان أحد الأهداف الرئيسية لعرض NG هو البحث عن أنفسنا خارج هذه الثقب وإتاحة نقل أعمال العرض، جزئيًا أو كليًا، إلى سلسلة تعليمات أخرى (أو سلاسل محادثات).

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

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

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

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