إعادة تصميم بنية "أدوات مطوري البرامج": تحديث البنية الأساسية لخدمة مقارنة الأسعار (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"
يمكنك استخدام الخيارات التالية لمناقشة الميزات أو التحديثات الجديدة أو أي معلومات أخرى متعلّقة بـ "أدوات مطوري البرامج".
- يمكنك إرسال الملاحظات وطلبات الميزات إلينا على crbug.com.
- يمكنك الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" باستخدام رمز خيارات إضافية > مساعدة > الإبلاغ عن مشكلة في "أدوات مطوّري البرامج" في "أدوات مطوّري البرامج".
- يمكنك نشر تغريدة على Twitter على @ChromeDevTools.
- يمكنك إضافة تعليقات على فيديوهات YouTube التي تعرض الميزات الجديدة في "أدوات مطوّري البرامج" أو فيديوهات YouTube التي تعرض نصائح حول "أدوات مطوّري البرامج".