تحديث البنية الأساسية لصفحات الأنماط المتتالية (CSS) في "أدوات مطوري البرامج"

إعادة تصميم بنية "أدوات مطوري البرامج": تحديث البنية الأساسية لخدمة مقارنة الأسعار (CSS) في "أدوات مطوري البرامج"

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

الحالة السابقة لملف CSS في "أدوات المطوّرين"

نفَّذت أدوات المطوّرين CSS بطريقتَين مختلفتَين: طريقة لملفات CSS المستخدَمة في الجزء القديم من أدوات المطوّرين، وطريقة أخرى لمكونات الويب الحديثة المستخدَمة في أدوات المطوّرين.

تم تحديد عملية تنفيذ CSS في "أدوات مطوّري البرامج" منذ عدة سنوات، وهي قديمَة الآن. تمّت إزالة هذه الملفات بعد أن تمّ استخدام نمط module.json في "أدوات مطوّري البرامج". إنّ القسم resources هو آخر عائق أمام إزالة هذه الملفات، ويُستخدَم لتحميل ملفات CSS.

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

تم اعتبار أي ملفات CSS كانت متوفّرة في DevTools "قديمة" لأنّه تم تحميلها باستخدام ملف module.json، والذي تتم إزالته حاليًا. يجب إدراج جميع ملفات CSS ضمن resources في ملف module.json في الدليل نفسه الذي يتضمّن ملف CSS.

مثال على ملف module.json متبقٍّ:

{
  "resources": [
    "serviceWorkersView.css",
    "serviceWorkerUpdateCycleView.css"
  ]
}

ستملء ملفات CSS هذه بعد ذلك خريطة عناصر عالمية تُسمى Root.Runtime.cachedResources كعملية ربط من مسار إلى محتوياتها. لإضافة أنماط إلى DevTools، عليك استدعاء registerRequiredCSS باستخدام المسار الدقيق للملف الذي تريد تحميله.

مثال على registerRequiredCSS طلب:

constructor() {
  
  this.registerRequiredCSS('ui/legacy/components/quick_open/filteredListWidget.css');
  
}

سيؤدي ذلك إلى استرداد محتوى ملف CSS وإدراجه كعنصر <style> في الصفحة باستخدام الدالة appendStyle:.

دالة appendStyle التي تضيف CSS باستخدام عنصر نمط مضمّن:

const content = Root.Runtime.cachedResources.get(cssFile) || '';

if (!content) {
  console.error(cssFile + ' not preloaded. Check module.json');
}

const styleElement = document.createElement('style');
styleElement.textContent = content;
node.appendChild(styleElement);

عندما طرحنا مكوّنات الويب الحديثة (باستخدام العناصر المخصّصة)، قرّرنا في البداية استخدام CSS من خلال علامات <style> مضمّنة في ملفات المكوّنات نفسها. وقد واجهنا بعض التحديات في هذا الشأن:

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

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

البحث عن الحلول المحتملة

يمكن تقسيم المشكلة إلى قسمَين مختلفَين:

  • معرفة كيفية تعامل نظام الإصدار مع ملفات CSS
  • معرفة كيفية استيراد ملفات CSS واستخدامها من خلال "أدوات المطوّر"

لقد راجعنا الحلول المحتملة المختلفة لكل جزء، وهي موضّحة أدناه.

استيراد ملفات CSS

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

لهذه الأسباب، لم تكن عبارات @import وعلامات هي الخيار المناسب لاستخدامها في "أدوات مطوّري البرامج". ولن تكون متسقة مع عمليات الاستيراد في بقية أدوات المطوّرين، ما سيؤدي إلى ظهور وميض محتوى غير مُنمَّط (FOUC). سيكون نقل البيانات إلى CSS Module Scripts أكثر صعوبة لأنّه يجب إضافة عمليات الاستيراد صراحةً والتعامل معها بشكل مختلف عن التعامل مع علامات <link>.

const output = LitHtml.html`
<style> @import "css/styles.css"; </style>
<button> Hello world </button>`
const output = LitHtml.html`
<link rel="stylesheet" href="styles.css">
<button> Hello World </button>`

الحلول المحتملة باستخدام @import أو <link>

وبدلاً من ذلك، اخترنا العثور على طريقة لاستيراد ملف CSS كعنصر CSSStyleSheet حتى نتمكّن من إضافته إلى Shadow DOM (تستخدم أدوات المطوّرين Shadow DOM منذ بضع سنوات) باستخدام سمة adoptedStyleSheets.

خيارات أداة تجميع التطبيقات

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

بدلاً من ذلك، اطّلعنا على خيار استخدام نظام GN الحالي لتنفيذ عملية التحويل هذه نيابةً عنا.

البنية الأساسية الجديدة لاستخدام CSS في أدوات مطوّري البرامج

يتضمّن الحلّ الجديد استخدام adoptedStyleSheets لإضافة أنماط إلى Shadow DOM معيّن أثناء استخدام نظام GN للإنشاء لإنشاء عناصر CSSStyleSheet التي يمكن أن تتبنّاها document أو ShadowRoot.

// CustomButton.ts

// Import the CSS style sheet contents from a JS file generated from CSS
import customButtonStyles from './customButton.css.js';
import otherStyles from './otherStyles.css.js';

export class CustomButton extends HTMLElement{
  
  connectedCallback(): void {
    // Add the styles to the shadow root scope
    this.shadow.adoptedStyleSheets = [customButtonStyles, otherStyles];
  }
}

هناك مزايا متعددة لاستخدام adoptedStyleSheets، بما في ذلك:

  • وهو قيد التطوير ليصبح معيارًا حديثًا للويب.
  • منع تكرار محتوى CSS
  • تُطبِّق الأنماط على Shadow DOM فقط، ما يتجنّب أي مشاكل ناتجة عن أسماء الفئات المكرّرة أو أدوات اختيار الأرقام التعريفية في ملفات CSS.
  • سهولة نقل البيانات إلى معايير الويب المستقبلية، مثل نصوص CSS Module Scripts وImport Assertions

كان التحذير الوحيد من الحلّ هو أنّ عبارات import تتطلّب استيراد ملف .css.js. للسماح لـ GN بإنشاء ملف CSS أثناء عملية الإنشاء، كتبنا البرنامج النصي generate_css_js_files.js. يعالج نظام الإنشاء الآن كل ملف CSS ويحوّله إلى ملف JavaScript يصدِّر تلقائيًا عنصر CSSStyleSheet. هذا أمر رائع لأنّه يمكننا استيراد ملف CSS واستخدامه بسهولة. بالإضافة إلى ذلك، يمكننا الآن أيضًا تصغير إصدار الإنتاج بسهولة، ما يحدّ من حجم الملف:

const styles = new CSSStyleSheet();
styles.replaceSync(
  // In production, we also minify our CSS styles
  /`${isDebug ? output : cleanCSS.minify(output).styles}
  /*# sourceURL=${fileName} */`/
);

export default styles;

مثال على iconButton.css.js تم إنشاؤه من النص البرمجي

نقل الرموز القديمة باستخدام قواعد ESLint

على الرغم من أنّه يمكن نقل مكونات الويب يدويًا بسهولة، كانت عملية نقل الاستخدامات القديمة من registerRequiredCSS أكثر تعقيدًا. كانت الدالتان الرئيسيتان اللتان سجّلتا الأنماط القديمة هما registerRequiredCSS وcreateShadowRootWithCoreStyles. وبما أنّ خطوات نقل هذه الطلبات كانت آلية إلى حدٍ كبير، قرّرنا أنّه يمكننا استخدام قواعد ESLint لتطبيق الإصلاحات ونقل الرموز القديمة تلقائيًا. تستخدِم أدوات المطوّرين حاليًا عددًا من القواعد المخصّصة لقاعدة بيانات أدوات المطوّرين. وقد كان ذلك مفيدًا لأنّ ESLint يُحلِّل الرمز البرمجي إلى شجرة بنية نحوية مجردة(اختصارًا AST) وأصبح بإمكاننا طلب بيانات عقد المكالمات المحدّدة التي كانت طلبات لتسجيل خدمة CSS.

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

من بين عيوب استخدام قواعد ESLint لنقل البيانات أنّه لم نتمكّن من تغيير ملف GN build المطلوب في النظام. وكان على المستخدم إجراء هذه التغييرات يدويًا في كل دليل. على الرغم من أنّ هذا الإجراء يتطلّب المزيد من العمل، إلا أنّه كان طريقة جيدة للتأكّد من أنّ كل ملف .css.js يتم استيراده يتم إنشاؤه فعليًا من خلال نظام الإنشاء.

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

ماذا بعد ذلك؟

حتى الآن، تم نقل جميع مكوّنات الويب في أدوات مطوّري البرامج في Chromium لاستخدام البنية الأساسية الجديدة لتنسيق CSS بدلاً من استخدام الأنماط المضمّنة. وتم أيضًا نقل معظم الاستخدامات القديمة لـ registerRequiredCSS لاستخدام النظام الجديد. كل ما عليك فعله الآن هو إزالة أكبر عدد ممكن من ملفات module.json ثم نقل هذه البنية الأساسية الحالية لتنفيذ نصوص CSS Module Scripts في المستقبل.

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

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

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

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