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

Philip Pfaffe
Ergün Erdogmus
Ergün Erdogmus

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

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

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

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

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

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

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

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

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

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

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

  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);

قم بمطابقة النتيجة لدالة color-mix.

المثال الأبسط يعمل بشكل جيد. أمّا في المثال الأكثر تعقيدًا، فتكون المطابقة <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: العرض من أعلى إلى أسفل

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

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

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

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

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

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

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

تأثير الأداء

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

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

نظرًا للمكاسب التي حققناها من النهج الجديد، قررنا المضي قدمًا فيه.

شكر وتقدير

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

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

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

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

استخدِم الخيارات التالية لمناقشة الميزات والتغييرات الجديدة في المشاركة أو مناقشة أي معلومات أخرى متعلّقة بأدوات مطوري البرامج.

  • يمكنك إرسال اقتراح أو ملاحظات إلينا عبر crbug.com.
  • الإبلاغ عن مشكلة في "أدوات مطوري البرامج" باستخدام خيارات إضافية   المزيد > مساعدة > الإبلاغ عن مشاكل في "أدوات مطوري البرامج" في "أدوات مطوري البرامج"
  • يمكنك نشر تغريدة على @ChromeDevTools.
  • شارِك في التعليقات على الميزات الجديدة في فيديوهات YouTube أو نصائح حول أدوات مطوّري البرامج فيديوهات YouTube.