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

هناك فرق كبير، أليس كذلك؟ في ما يلي تفاصيل التحسينات الرئيسية:
-
color-mix: معاينة مفيدة تمثّل بشكل مرئي وسيطتَي اللون ضمن الدالةcolor-mix -
pink: معاينة لون يمكن النقر عليها للون المُسمّىpinkانقر عليه لفتح علبة الألوان وإجراء تعديلات سهلة. var(--undefined, [fallback value]). تم تحسين معالجة المتغيّرات غير المحدّدة، حيث يظهر المتغيّر غير المحدّد باللون الرمادي وتظهر القيمة الاحتياطية النشطة (في هذه الحالة، لون HSL) مع معاينة لون قابلة للنقر.-
hsl(…): معاينة أخرى قابلة للنقر للون دالة اللونhsl، ما يتيح الوصول السريع إلى علبة الألوان. -
177deg: ساعة زاوية قابلة للنقر تتيح لك سحب قيمة الزاوية وتعديلها بشكل تفاعلي. -
var(--saturation, …): رابط قابل للنقر يؤدي إلى تعريف الخاصية المخصّصة، ما يسهّل الانتقال إلى البيان ذي الصلة.
الفرق مذهل. لتحقيق ذلك، كان علينا تعليم "أدوات مطوري البرامج في Chrome" فهم قيم سمات CSS بشكل أفضل بكثير من ذي قبل.
ألم تكن هذه المعاينات متاحة من قبل؟
على الرغم من أنّ رموز المعاينة هذه قد تبدو مألوفة، لم يتم عرضها دائمًا بشكل متّسق، خاصةً في بنية CSS المعقّدة مثل المثال أعلاه. وحتى في الحالات التي كانت تعمل فيها، كان الأمر يتطلّب في كثير من الأحيان جهدًا كبيرًا لضمان عملها بشكل صحيح.
والسبب في ذلك هو أنّ نظام تحليل القيم قد تطوّر بشكل طبيعي منذ الأيام الأولى لأدوات مطوري البرامج في Chrome. ومع ذلك، لم يتمكّن من مواكبة الميزات الجديدة الرائعة التي نحصل عليها مؤخرًا من CSS، والزيادة المقابلة في تعقيد اللغة. كان النظام يتطلّب إعادة تصميم كاملة لمواكبة التطوّر، وهذا ما فعلناه بالضبط.
كيفية معالجة قيم سمات CSS
في "أدوات مطوري البرامج في Chrome"، تنقسم عملية عرض وتزيين تعريفات السمات في علامة التبويب الأنماط إلى مرحلتَين مختلفتَين:
- التحليل الهيكلي تُحلّل هذه المرحلة الأولية بيان السمة لتحديد مكوّناتها الأساسية وعلاقاتها. على سبيل المثال، في التعريف
border: 1px solid red، سيتعرّف على1pxكطول، وsolidكسلسلة، وredكلون. - العرض استنادًا إلى التحليل البنيوي، تحوّل مرحلة العرض هذه المكوّنات إلى تمثيل HTML. يؤدي ذلك إلى إثراء نص السمة المعروض بعناصر تفاعلية وإشارات مرئية. على سبيل المثال، يتم عرض قيمة اللون
redباستخدام رمز لون قابل للنقر، وعند النقر عليه، تظهر علبة ألوان لتعديل اللون بسهولة.
التعبيرات العادية
في السابق، كنا نعتمد على التعبيرات النمطية (regexes) لتحليل قيم السمات من أجل إجراء تحليل بنيوي. احتفظنا بقائمة من التعبيرات النمطية لمطابقة أجزاء قيم السمات التي اعتبرناها مزخرفة. على سبيل المثال، كانت هناك تعبيرات تتطابق مع ألوان 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، يعني ذلك استخدام محلل لغوي للغات الخالية من السياق. في الواقع، كان نظام المحلّل اللغوي هذا متوفّرًا في قاعدة الرموز البرمجية "أدوات مطوري البرامج": Lezer من CodeMirror، وهو الأساس الذي يستند إليه، على سبيل المثال، تمييز البنية في CodeMirror، وهو المحرِّر الذي يظهر في لوحة المصادر. أتاح لنا المحلل اللغوي لـ CSS في Lezer إنشاء أشجار بنية (غير مجردة) لقواعد CSS وكان جاهزًا للاستخدام. علامة النصر

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

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

بالنسبة إلى الشجرة النموذجية، إليك الترتيب الذي يتم به عرض قيمة السمة:
- انتقِل إلى استدعاء الدالة
hsl. تمت المطابقة، لذا يتم استدعاء دالة عرض الألوان. وهي تؤدي وظيفتَين:- يتم احتساب قيمة اللون الفعلية باستخدام آلية الاستبدال الفوري لأي وسيطات
var، ثم يتم رسم رمز لون. - يعرض العناصر الفرعية لـ
CallExpressionبشكل متكرر. يؤدي ذلك تلقائيًا إلى عرض اسم الدالة والأقواس والفواصل، وهي مجرد نص.
- يتم احتساب قيمة اللون الفعلية باستخدام آلية الاستبدال الفوري لأي وسيطات
- انتقِل إلى الوسيطة الأولى لطلب
hsl. تمت المطابقة، لذا يتم استدعاء عارض الزاوية، الذي يرسم رمز الزاوية ونص الزاوية. - انتقِل إلى الوسيطة الثانية، وهي استدعاء
var. تمت المطابقة، لذا استدعِ المتغيّر renderer الذي يعرض ما يلي:- النص
var(في البداية - اسم المتغيّر ويتم تزيينه إما برابط يؤدي إلى تعريف المتغيّر أو بلون نص رمادي للإشارة إلى أنّه غير محدّد، كما يضيف نافذة منبثقة إلى المتغيّر لعرض معلومات عن قيمته.
- الفاصلة، ثم يعرض القيمة الاحتياطية بشكل متكرر.
- قوس إغلاق
- النص
- انتقِل إلى الوسيطة الأخيرة لطلب
hsl. لم يتطابق، لذا ما عليك سوى إخراج محتوياته النصية.
هل لاحظت أنّه في هذه الخوارزمية، يتحكّم العارض بشكل كامل في كيفية عرض العناصر الثانوية لعقدة مطابقة؟ يُعدّ عرض العناصر الفرعية بشكل متكرّر إجراءً استباقيًا. هذه الحيلة هي ما مكّن من الانتقال خطوة بخطوة من العرض المستند إلى التعبير النمطي إلى العرض المستند إلى شجرة البنية. بالنسبة إلى العُقد التي تمت مطابقتها مع أداة مطابقة قديمة تستند إلى التعبير النمطي، يمكن استخدام أداة العرض المقابلة في شكلها الأصلي. من حيث شجرة البنية، ستتحمل مسؤولية عرض الشجرة الفرعية بأكملها، ويمكن توصيل نتيجتها (عقدة HTML) بشكل نظيف في عملية العرض المحيطة. أتاح لنا ذلك نقل أدوات المطابقة والعرض في أزواج، وتبديلها واحدًا تلو الآخر.
هناك ميزة أخرى رائعة في أدوات العرض التي تتحكم في عرض العناصر الفرعية للعقدة المطابقة وهي أنها تمنحنا القدرة على التفكير في التبعيات بين الرموز التي نضيفها. في المثال أعلاه، يعتمد اللون الذي تنتجه الدالة hsl بشكل واضح على قيمة تدرج اللون. وهذا يعني أنّ اللون الذي يظهر في رمز اللون يعتمد على الزاوية التي تظهر في رمز الزاوية. إذا فتح المستخدم محرِّر الزاوية من خلال هذا الرمز وعدّل الزاوية، يمكننا الآن تعديل لون رمز اللون في الوقت الفعلي:
كما هو موضّح في المثال أعلاه، نستخدم هذه الآلية أيضًا مع أزواج الرموز الأخرى، مثل color-mix() وقناتَي الألوان الخاصتَين به، أو دوال var التي تعرض لونًا من اللون الاحتياطي.
التأثير على الأداء
عند التعمّق في هذه المشكلة لتحسين الموثوقية وحلّ المشاكل المستمرة منذ فترة طويلة، كنا نتوقع بعض التراجع في الأداء نظرًا لأننا بدأنا في تشغيل محلّل لغوي متكامل. لاختبار ذلك، أنشأنا مقياس أداء يعرض حوالي 3.5 آلاف تعريف للسمات، وحدّدنا الملفات الشخصية لكل من الإصدارات المستندة إلى التعبيرات النمطية والمستندة إلى المحلّل اللغوي مع تقييد السرعة بمقدار 6 أضعاف على جهاز M1.
As we expected, the parsing-based approach turned out to be 27% slower than the regex-based approach for that case. استغرق النهج المستند إلى التعبير النمطي 11 ثانية للعرض، بينما استغرق النهج المستند إلى المحلل اللغوي 15 ثانية للعرض.
بالنظر إلى المكاسب التي نحصل عليها من النهج الجديد، قررنا المضي قدمًا فيه.
الإقرارات
نتقدّم بخالص الشكر إلى "صوفيا إميليانوفا" و"جيسيلين يين" على مساعدتهما القيّمة في تعديل هذا المنشور.
تنزيل قنوات المعاينة
ننصحك باستخدام إصدار Chrome Canary أو Dev أو Beta كمتصفّح تلقائي للتطوير. تتيح لك قنوات المعاينة هذه الوصول إلى أحدث ميزات "أدوات مطوّري البرامج"، وتسمح لك باختبار واجهات برمجة التطبيقات المتطوّرة لمنصات الويب، وتساعدك في العثور على المشاكل في موقعك الإلكتروني قبل أن يكتشفها المستخدمون.
التواصل مع فريق "أدوات مطوّري البرامج في Chrome"
استخدِم الخيارات التالية لمناقشة الميزات الجديدة أو التحديثات أو أي شيء آخر يتعلّق بـ "أدوات مطوّري البرامج".
- يمكنك إرسال الملاحظات وطلبات الميزات إلينا على crbug.com.
- يمكنك الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" من خلال النقر على خيارات إضافية > مساعدة > الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" في "أدوات مطوّري البرامج".
- يمكنك نشر تغريدة على @ChromeDevTools.
- يمكنك إضافة تعليقات على فيديوهات على YouTube حول الميزات الجديدة في "أدوات مطوّري البرامج" أو فيديوهات على YouTube حول نصائح "أدوات مطوّري البرامج".