تجاوز التعبيرات العادية: تحسين تحليل قيم CSS في "أدوات مطوري البرامج في Chrome"

Philip Pfaffe
Ergün Erdogmus
Ergün Erdogmus

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

لنقارن بين "أدوات المطوّر" الحالية والإصدار السابق:

في الجزء العلوي: هو أحدث إصدار من Chrome، في الجزء السفلي: Chrome 121.

فرق كبير، أليس كذلك؟ في ما يلي تفاصيل التحسينات الرئيسية:

  • color-mix: معاينة مفيدة تمثّل بصريًا وسيطتَي اللون ضمن دالة color-mix.
  • pink. معاينة لون قابلة للنقر للون المسمى pink. انقر عليه لفتح أداة اختيار الألوان لإجراء تعديلات بسهولة.
  • var(--undefined, [fallback value]). تحسين معالجة المتغيّرات غير المحدّدة، مع عرض المتغيّر غير المحدّد غير مفعّل والقيمة الاحتياطية النشطة (في هذه الحالة، لون HSL) مع عرض معاينة لون قابلة للنقر.
  • hsl(…): معاينة أخرى للألوان يمكن النقر عليها لدالة الألوان hsl، ما يتيح الوصول السريع إلى أداة اختيار الألوان.
  • 177deg: ساعة زاوية قابلة للنقر تتيح لك سحب قيمة الزاوية وتعديلها بشكل تفاعلي.
  • var(--saturation, …): رابط قابل للنقر يؤدي إلى تعريف السمة المخصّصة، ما يسهّل الانتقال إلى البيان ذي الصلة.

الاختلاف مذهل. لتحقيق ذلك، كان علينا تعليم DevTools فهم قيم سمات CSS بشكل أفضل بكثير من ذي قبل.

ألم تكن هذه المعاينات متاحة بالفعل؟

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

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

كيفية معالجة قيم خصائص CSS

في DevTools، تنقسم عملية عرض وتزيين بيانات السمات في علامة التبويب الأنماط إلى مرحلتين مختلفتَين:

  1. التحليل الهيكلي. تحلِّل هذه المرحلة الأولية بيان الموقع لتحديد مكوّناته الأساسية وعلاقاتها. على سبيل المثال، في البيان border: 1px solid red، سيتعرّف على 1px كطول وsolid كسلسلة وred كبُنيّة لون.
  2. العرض واستنادًا إلى التحليل الهيكلي، تحوِّل مرحلة العرض هذه المكوّنات إلى تمثيل HTML. ويؤدي ذلك إلى إثراء نص الفندق المعروض بعناصر تفاعلية وإشارات مرئية. على سبيل المثال، يتم عرض قيمة اللون red باستخدام رمز لون قابل للنقر يؤدي عند النقر عليه إلى إظهار أداة اختيار ألوان لتسهيل التعديل.

التعبيرات العادية

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

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

جارٍ مطابقة color-mix()

التعبير العادي الذي استخدمناه مع الدالة color-mix() كان ما يلي:

/color-mix\(.*,\s*(?<firstColor>.+)\s*,\s*(?<secondColor>.+)\s*\)/g

والتي تتطابق مع بنية الجملة:

color-mix(<color-interpolation-method>, [<color> && <percentage [0,100]>?]#{2})

جرِّب تشغيل المثال التالي لعرض المطابقات.

const re = /color-mix\(.*,\s*(?<firstColor>.+)\s*,\s*(?<secondColor>.+)\s*\)/g;

// it works - simpler example
const simpler = re.exec('color-mix(in srgb, pink, hsl(127deg 100% 50%))');
console.table(simpler.groups);

re.exec('');

// it doesn't work - complex example
const complex = re.exec('color-mix(in srgb, pink, var(--undefined, hsl(127deg var(--saturation, 100%) 50%)))');
console.table(complex.groups);

مطابقة نتيجة دالة مزج الألوان

يعمل المثال البسيط بشكل جيد. في المثال الأكثر تعقيدًا، تكون مطابقة <firstColor> هي hsl(177deg var(--saturation ومطابقة <secondColor> هي 100%) 50%))، ما لا معنى له على الإطلاق.

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

جارٍ مطابقة tan()

كان أحد الأخطاء الأكثر طرافة التي تم الإبلاغ عنها متعلقًا بالدالة المثلثية tan(). كان التعبير العادي الذي كنا نستخدمه لمطابقة الألوان يتضمّن تعبيرًا فرعيًا \b[a-zA-Z]+\b(?!-) لمطابقة الألوان المُسمّاة، مثل الكلمة الرئيسية red. بعد ذلك، تحقّقنا ممّا إذا كان الجزء المطابق هو لون مُعنوَن، وتبيّن أنّ tan هو لون مُعنوَن أيضًا. وبالتالي، فسّرنا تعبيرات tan() على أنّها ألوان.

المطابقة var()

لنلقِ نظرة على مثال آخر، وهو دوال var() التي تحتوي على عنصر احتياطي يحتوي على مراجع var() أخرى: var(--non-existent, var(--margin-vertical)).

سيتطابق التعبير العادي لسمة var() مع هذه القيمة. إلا أنّه سيتوقّف عن المطابقة عند أول قوس إغلاق. وبالتالي، تتم مطابقة النص أعلاه على النحو التالي: var(--non-existent, var(--margin-vertical). هذا هو أحد القيود الأساسية لمطابقة التعبير العادي. إنّ اللغات التي تتطلّب مطابقة الأقواس ليست منتظمة بشكل أساسي.

الانتقال إلى محلل CSS

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

شجرة بنية لقيمة السمة hsl(177deg var(--saturation, 100%) 50%)‏. وهي نسخة مبسّطة من النتيجة التي ينتج عنها محلّل Lezer، مع استبعاد العقد النحوية البحتة للفواصل والقوسين.

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

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

المرحلة 1: المطابقة من الأسفل إلى الأعلى

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

راجِع مثال شجرة البنية أعلاه:

المرحلة 1: المطابقة من الأسفل إلى الأعلى في شجرة البنية

بالنسبة إلى هذه الشجرة، سيتم تطبيق المطابقات بالترتيب التالي:

  1. hsl(177degvar(--saturation, 100%) 50%): أولاً، نتعرّف على الوسيطة الأولى لاستدعاء الدالة hsl، وهي زاوية درجة اللون. ونطابقها بمطابقة الزوايا، بحيث يمكننا تزيين قيمة الزاوية برمز الزاوية.
  2. hsl(177degvar(--saturation, 100%)50%): ثانيًا، نرصد طلب استدعاء الدالة var باستخدام مطابق var. لهذا النوع من المكالمات، نودّ بشكل أساسي اتّخاذ إجراءين:
    • ابحث عن بيان المتغيّر واحسب قيمته، وأضِف رابطًا وإطار معلومات منبثقًا إلى اسم المتغيّر للربط بهما على التوالي.
    • يمكنك تزيين الطلب برمز لون إذا كانت القيمة المحسوبة لونًا. هناك نقطة ثالثة، ولكن سنتحدث عنها لاحقًا.
  3. hsl(177deg var(--saturation, 100%) 50%): أخيرًا، نطابق تعبير الاستدعاء لدالة hsl حتى نتمكّن من تزيينها برمز اللون.

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

المرحلة 2: التقديم من الأعلى إلى الأسفل

في المرحلة الثانية، نعكس الاتجاه. استنادًا إلى نتائج المطابقة من المرحلة 1، نعرض الشجرة بتنسيق HTML من خلال التنقّل فيها بالترتيب من الأعلى إلى الأسفل. بالنسبة إلى كل عقدة تمت زيارتها، نتحقّق مما إذا كانت مطابقة، وإذا كان الأمر كذلك، نُطلِب من أداة العرض المقابلة للمطابق معالجة العقدة. نتجنب الحاجة إلى معالجة خاصة للعقد التي تحتوي على نص فقط (مثل NumberLiteral "‎50%") من خلال تضمين أداة مطابقة وعرض تلقائية للعقد النصية. تُخرج العارضات ببساطة عُقد HTML، والتي عند تجميعها معًا، تُنتج تمثيل قيمة السمة، بما في ذلك زخارفها.

المرحلة 2: المعالجة من الأعلى إلى الأسفل في شجرة البنية

بالنسبة إلى شجرة النموذج، إليك ترتيب عرض قيمة السمة:

  1. انتقِل إلى استدعاء الدالة hsl. تطابقت، لذا استدعى أداة عرض دالة الألوان. ويؤدي ذلك إلى إجراءَين:
    • تحسب قيمة اللون الفعلية باستخدام آلية الاستبدال الفوري لأي وسيطة في var، ثم ترسم رمز لون.
    • يعرض العناصر الثانوية في CallExpression بشكل متكرر. يُتولى هذا الإجراء تلقائيًا عرض اسم الدالة والأقواس والفواصل، وهي مجرد نص.
  2. انتقل إلى الوسيطة الأولى لطلب hsl. تطابقت القيمة، لذا استدعى معالِج الزاوية الذي يرسم رمز الزاوية ونصه.
  3. انتقِل إلى الوسيطة الثانية، وهي طلب var. تطابقت القيمة، لذا استدِع المتغيّر المشغِّل الذي يعرض ما يلي:
    • النص var( في البداية.
    • اسم المتغيّر وتزيينه إما برابط يؤدي إلى تعريف المتغيّر أو بلون نص رمادي للإشارة إلى أنّه غير محدّد. ويضيف أيضًا نافذة منبثقة إلى المتغيّر لعرض معلومات عن قيمته.
    • تعرض الفاصلة ثم تعرض القيمة الاحتياطية بشكل متكرر.
    • قوس إغلاق
  4. انتقِل إلى الوسيطة الأخيرة لاستدعاء hsl. لم تكن متطابقة، لذا أخرج محتوياتها النصية فقط.

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

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

كما هو موضّح في المثال أعلاه، نستخدم هذه الآلية مع مجموعات الرموز الأخرى أيضًا، مثل color-mix() وقناتَي ألوانه، أو وظائف var التي تعرض لونًا من القيمة الاحتياطية.

التأثير على الأداء

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

كما توقّعنا، تبيّن أنّ المنهج المستنِد إلى التحليل كان أبطأ بنسبة% 27 من المنهج المستنِد إلى التعبير العادي في هذه الحالة. استغرق النهج المستنِد إلى التعبير العادي 11 ثانية للعرض، واستغرق النهج المستنِد إلى التحليل 15 ثانية للعرض.

ونظرًا للفوائد التي نحصل عليها من هذا النهج الجديد، قرّرنا المضي قدمًا به.

الشكر والتقدير

نشكر بشدة صوفيا إميليانوفا وجيسيلين يين على مساعدتهما القيّمة في تعديل هذا المنشور.

تنزيل قنوات المعاينة

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

التواصل مع فريق "أدوات مطوّري البرامج في Chrome"

يمكنك استخدام الخيارات التالية لمناقشة الميزات أو التحديثات الجديدة أو أي معلومات أخرى متعلّقة بـ "أدوات مطوري البرامج".