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

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

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

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

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

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

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

تم اعتبار أي ملفات CSS كانت متوفّرة في "أدوات مطوري البرامج" بأنّها "قديمة" لأنّه تم تحميلها باستخدام ملف 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 Scripts.

لهذه الأسباب، لم تكن عبارات @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 بسهولة، ما يعني أنّه يمكننا أيضًا معالجة حالات الحدّ المتعددة في القاعدة وإصلاحها بشكل تلقائي باستخدام واجهة برمجة التطبيقات لأداة إصلاح ESLint.

ماذا بعد ذلك؟

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

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

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

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

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