רענון הארכיטקטורה של כלי הפיתוח: מודרניזציה של תשתית CSS בכלי הפיתוח
הפוסט הזה הוא חלק מסדרת פוסטים בבלוג שמתארים את השינויים שאנחנו מבצעים בארכיטקטורה של DevTools ואת אופן הבנייה שלו. נסביר איך CSS פעל ב-DevTools בעבר ואיך עדכנו את ה-CSS ב-DevTools לקראת המעבר (בסופו של דבר) לפתרון סטנדרטי לאינטרנט לטעינת CSS בקובצי JavaScript.
המצב הקודם של CSS בכלי הפיתוח
ה-CSS הוטמע בכלי הפיתוח בשתי דרכים שונות: אחת לקובצי CSS שנעשה בהם שימוש בחלק הקודם של כלי הפיתוח, ואחת לרכיבי האינטרנט המודרניים שנעשה בהם שימוש בכלי הפיתוח.
הטמעת ה-CSS ב-DevTools הוגדרה לפני שנים רבות והיא לא עדכנית. בכלי הפיתוח אנחנו משתמשים בתבנית 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 עצמאיים.
- אתגר בקיצור קוד לא ניתן היה להקטין בקלות את ה-CSS שקודם לקוד, ולכן אף אחד מה-CSS לא עבר הקטנה. גודל הקובץ של גרסה ה-build של DevTools גדל גם בגלל ה-CSS הכפול שנוצר על ידי כמה מופעים של אותו רכיב אינטרנט.
מטרת הפרויקט שלי בתקופת ההתמחות הייתה למצוא פתרון לתשתית ה-CSS שעובד גם עם התשתית הקודמת וגם עם רכיבי האינטרנט החדשים שמשמשים את DevTools.
מחקר פתרונות אפשריים
אפשר לחלק את הבעיה לשני חלקים שונים:
- להבין איך מערכת ה-build מטפלת בקובצי CSS.
- להבין איך קובצי ה-CSS מיובאים ומשמשים את DevTools.
בדקנו פתרונות פוטנציאליים שונים לכל חלק, והם מפורטים בהמשך.
ייבוא קובצי CSS
היעד של ייבוא CSS ושימוש בו בקובצי TypeScript היה להתקרב ככל האפשר לתקני האינטרנט, לאכוף עקביות בכל DevTools ולהימנע מכפילות של CSS ב-HTML שלנו. בנוסף, רצינו לבחור פתרון שיאפשר לנו להעביר את השינויים שלנו לתקנים חדשים של פלטפורמות אינטרנט, כמו סקריפטים של מודולים של CSS.
לכן, משפטי @import ותגי לא נראו מתאימים ל-DevTools. הם לא יהיו אחידים עם ייבוא בשאר כלי הפיתוח, ויגרמו להצגה קצרה של תוכן ללא עיצוב (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 (DevTools משתמש ב-Shadow DOM כבר כמה שנים) באמצעות המאפיין adoptedStyleSheets
שלו.
אפשרויות של חבילה
נזקקנו לדרך להמיר קובצי CSS לאובייקט CSSStyleSheet
כדי שנוכל לבצע בו שינויים בקלות בקובץ TypeScript. בדקנו את Rollup ואת webpack כחבילות פוטנציאליות שיכולות לבצע את הטרנספורמציה הזו בשבילנו. כבר משתמשים ב-Rollup ב-DevTools בגרסה ל-production, אבל הוספת אחת מהחבילות לגרסה ל-production עלולה לגרום לבעיות ביצועים פוטנציאליות כשעובדים עם מערכת ה-build הנוכחית שלנו. השילוב שלנו עם מערכת ה-build של GN ב-Chromium מקשה על היצירה של חבילות, ולכן חבילות נוטים לא להשתלב היטב עם מערכת ה-build הנוכחית של Chromium.
במקום זאת, בדקנו את האפשרות להשתמש במערכת ה-build הנוכחית של GN כדי לבצע את הטרנספורמציה הזו בשבילנו.
התשתית החדשה לשימוש ב-CSS בכלי הפיתוח
הפתרון החדש כולל שימוש ב-adoptedStyleSheets
כדי להוסיף סגנונות ל-Shadow DOM מסוים, תוך שימוש במערכת ה-build של 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
- החלת סגנונות רק על DOM בצל, וכך מונעים בעיות שנגרמות כתוצאה משמות כפולים של מחלקות או סלקטורים של מזהי קובצי CSS
- קל לעבור לתקני אינטרנט עתידיים, כמו סקריפטים של מודולים של CSS וטענות נכוֹנוּת (assertions) של ייבוא
הבעיה היחידה בפתרון הייתה שהצהרות import
דרשו ייבוא של הקובץ .css.js
. כדי לאפשר ל-GN ליצור קובץ CSS במהלך ה-build, כתבנו את הסקריפט generate_css_js_files.js
. מערכת ה-build מעבדת עכשיו כל קובץ CSS וממירה אותו לקובץ JavaScript, שמייצא כברירת מחדל אובייקט CSSStyleSheet
. זה נהדר כי אנחנו יכולים לייבא את קובץ ה-CSS ולהשתמש בו בקלות. בנוסף, עכשיו אנחנו יכולים גם למזער בקלות את גרסת ה-build של סביבת הייצור, וכך לחסוך בגודל הקובץ:
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 כבר מנתח את הקוד לAbstract Syntax Tree(ראשי תיבות: AST). AST), ויכולנו לשלוח שאילתות לצמתי הקריאה הספציפיים שהיו קריאות לשירותי CSS שרשומים.
הבעיה הגדולה ביותר שבה נתקלת כשכתבנו את כללי ESLint להעברה הייתה לכסות מקרים קיצוניים. רצינו לוודא שאנחנו מוצאים את האיזון הנכון בין הידיעה אילו מקרים קיצוניים כדאי לתעד ואילו כדאי להעביר באופן ידני. בנוסף, רצינו לוודא שנוכל להודיע למשתמש מתי קובץ .css.js
מיובאים לא נוצר באופן אוטומטי על ידי מערכת ה-build, כי כך נוכל למנוע שגיאות מסוג 'הקובץ לא נמצא' בזמן הריצה.
אחד החסרונות של השימוש בכללי ESLint במהלך ההעברה היה שלא הצלחנו לשנות את קובץ ה-build הנדרש של GN במערכת. המשתמש היה צריך לבצע את השינויים האלה באופן ידני בכל ספרייה. אמנם הדרך הזו דרשה יותר עבודה, אבל היא הייתה דרך טובה לוודא שכל קובץ .css.js
שייבאתם נוצר בפועל על ידי מערכת ה-build.
באופן כללי, השימוש בכללי ESLint להעברה הזו היה מאוד שימושי, כי הצלחנו להעביר במהירות את הקוד הקודם לתשתית החדשה. בנוסף, העובדה שה-AST זמין באופן מיידי אפשרה לנו לטפל במספר מקרים קיצוניים בכלל ולתקן אותם באופן אוטומטי ואמין באמצעות ממשק ה-API של תיקון הקוד של ESLint.
מה הדבר הבא?
עד כה, כל רכיבי האינטרנט בכלי הפיתוח של Chromium הועברו לשימוש בתשתית ה-CSS החדשה במקום להשתמש בסגנונות מוטמעים. גם רוב השימושים הקודמים ב-registerRequiredCSS
הועברו לשימוש במערכת החדשה. כל מה שנותר הוא להסיר כמה שיותר קבצים מסוג module.json
, ולאחר מכן להעביר את התשתית הנוכחית כדי להטמיע סקריפטים של מודולים של CSS בעתיד.
הורדת הערוצים לתצוגה מקדימה
מומלץ להשתמש ב-Chrome Canary, ב-Dev או ב-Beta כדפדפן הפיתוח שמוגדר כברירת מחדל. ערוצי התצוגה המקדימה האלה מעניקים לכם גישה לתכונות העדכניות ביותר של DevTools, מאפשרים לכם לבדוק ממשקי API מתקדמים לפלטפורמות אינטרנט ולמצוא בעיות באתר לפני שהמשתמשים שלכם יעשו זאת.
יצירת קשר עם צוות כלי הפיתוח ל-Chrome
אתם יכולים להשתמש באפשרויות הבאות כדי לדון בתכונות החדשות, בעדכונים או בכל דבר אחר שקשור ל-DevTools.
- אתם יכולים לשלוח לנו משוב ובקשות להוספת תכונות בכתובת crbug.com.
- מדווחים על בעיה בכלי הפיתוח באמצעות הסמל אפשרויות נוספות > עזרה > דיווח על בעיה בכלי הפיתוח ב-DevTools.
- שולחים ציוץ אל @ChromeDevTools.
- אפשר להשאיר תגובות בסרטונים של מה חדש בכלי הפיתוח ב-YouTube או בסרטונים של טיפים לכלי הפיתוח ב-YouTube.