תמיכה ב-CSS ב-JS בכלי הפיתוח

אלכס רודנקו
אלכס רודנקו

המאמר הזה עוסק בתמיכה ב-CSS ב-JS שנוספה ל-Chrome מגרסה 85 ואילך. באופן כללי, המשמעות של CSS-in-JS ובמה הוא שונה מ-CSS רגיל שנתמך על ידי כלי הפיתוח כבר זמן רב.

מהו CSS-in-JS?

ההגדרה של CSS-in-JS היא די מעורפלת. במובן הרחב, זוהי גישה לניהול קוד CSS באמצעות JavaScript. לדוגמה: יכול להיות שהתוכן ב-CSS מוגדר באמצעות JavaScript, ושהפלט הסופי של ה-CSS נוצר בזמן אמת על ידי האפליקציה.

בהקשר של כלי פיתוח, המשמעות של CSS-in-JS היא שתוכן ה-CSS מוחדר לדף באמצעות ממשקי API מסוג CSSOM. CSS רגיל מוחדר באמצעות רכיבי <style> או <link>, ויש לו מקור סטטי (למשל צומת DOM או משאב רשת). לעומת זאת, בדרך כלל ל-CSS ב-JS אין מקור סטטי. מקרה מיוחד במקרה הזה הוא שניתן לעדכן את התוכן של רכיב <style> באמצעות CSSOM API, מה שעלול לגרום לחוסר סנכרון של המקור עם גיליון הסגנונות של ה-CSS עצמו.

אם משתמשים בספריית CSS-in-JS (למשל styled-component, Emotion, JSS), הספרייה עשויה להחדיר סגנונות באמצעות ממשקי CSSOM API מאחורי הקלעים בהתאם למצב הפיתוח ולדפדפן.

נבחן כמה דוגמאות לאופן שבו ניתן להוסיף גיליון סגנונות באמצעות CSSOM API בדומה לאופן שבו ספריות CSS-in-JS פועלות.

// Insert new rule to an existing CSS stylesheet
const element = document.querySelector('style');
const stylesheet = element.sheet;
stylesheet.replaceSync('.some { color: blue; }');
stylesheet.insertRule('.some { color: green; }');

אפשר גם ליצור גיליון סגנונות חדש לגמרי:

// Create a completely new stylesheet
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync('.some { color: blue; }');
stylesheet.insertRule('.some { color: green; }');

// Apply constructed stylesheet to the document
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];

תמיכה ב-CSS בכלי הפיתוח

בכלי הפיתוח, התכונה הנפוצה ביותר לטיפול ב-CSS היא החלונית סגנונות. בחלונית סגנונות אתם יכולים לראות אילו כללים חלים על רכיב מסוים. אתם יכולים לערוך את הכללים ולראות את השינויים בדף בזמן אמת.

עד השנה שעברה, התמיכה בכללי CSS ששונו באמצעות ממשקי CSSOM API הייתה מוגבלת: הייתה לכם אפשרות לראות רק את הכללים המוחלים, אך לא הייתה לכם אפשרות לערוך אותם. המטרה העיקרית שלנו בשנה שעברה הייתה לאפשר עריכה של כללי CSS-in-JS באמצעות חלונית הסגנונות. לפעמים אנחנו גם קוראים לסגנונות CSS-in-JS "נבנו" כדי לציין שהם נבנו באמצעות ממשקי Web API.

תצוגה מפורטת יותר של פעולות עריכת סגנונות בכלי הפיתוח.

מנגנון לעריכת סגנונות בכלי הפיתוח

מנגנון לעריכת סגנונות בכלי הפיתוח

כשבוחרים רכיב בכלי פיתוח, מוצגת החלונית סגנונות. בחלונית סגנונות מפיקה פקודת CDP בשם CSS.getMatchedStylesForNode כדי לקבל כללי CSS שחלים על הרכיב. CDP הוא ראשי תיבות של Chrome DevTools Protocol וזהו API שמאפשר לממשק החזית של DevTools לקבל מידע נוסף על הדף שנבדק.

בעת ההפעלה, CSS.getMatchedStylesForNode מזהה את כל גיליונות הסגנונות במסמך ומנתח אותם באמצעות מנתח ה-CSS של הדפדפן. לאחר מכן היא בונה אינדקס שמשייך כל כלל CSS למיקום במקור בגיליון הסגנונות.

ייתכן שתשאלו, למה צריך לנתח שוב את ה-CSS? הבעיה כאן היא שמטעמי ביצועים, הדפדפן עצמו לא משנה את מיקומי המקור של כללי CSS, ולכן הוא לא מאחסן אותם. עם זאת, כלי הפיתוח צריכים את מיקומי המקור כדי לתמוך בעריכת CSS. אנחנו לא רוצים שמשתמשי Chrome רגילים ישלמו על פגיעה בביצועים, אבל אנחנו רוצים שלמשתמשים של כלי הפיתוח תהיה גישה למיקומי המקור. הגישה של ניתוח מחדש מתייחסת לשני תרחישים לדוגמה עם חסרונות מינימליים.

לאחר מכן, ההטמעה של CSS.getMatchedStylesForNode תבקש ממנוע הסגנון של הדפדפן לספק כללי CSS שתואמים לרכיב הנתון. לבסוף, השיטה משייכת את הכללים שהוחזרו על ידי מנוע הסגנון עם קוד המקור, ומספקת תשובה מובנית לגבי כללי CSS כדי שכלי הפיתוח יידעו איזה חלק מהכלל משמש כבורר או במאפיינים. הפעולה הזו מאפשרת לכלי הפיתוח לערוך את הסלקטור ואת המאפיינים בנפרד.

עכשיו נתחיל בעריכה. חשוב לזכור ש-CSS.getMatchedStylesForNode מחזיר את מיקומי המקור לכל כלל? זה חיוני לעריכה. כשמשנים כלל, כלי הפיתוח מנפיקים פקודת CDP אחרת שמעדכנת בפועל את הדף. הפקודה כוללת את המיקום המקורי של הקטע של הכלל שמתעדכן, ואת הטקסט החדש שצריך לעדכן באמצעותו.

בקצה העורפי, במהלך הטיפול בקריאה לערוך, כלי הפיתוח יעדכנו את גיליון הסגנונות של היעד. בנוסף, הפעולה הזו מעדכנת את העותק של המקור בגיליון הסגנונות שמתחזק, ומעדכנת את מיקומי המקור של הכלל המעודכן. בתגובה לקריאה לעריכה, החזית של כלי הפיתוח מחזירה את המיקומים המעודכנים של קטע הטקסט שעודכן עכשיו.

זו הסיבה לכך שעריכת CSS-in-JS ב-DevTools לא פועלת בצורה מוכנה: ל-CSS-in-JS אין מקור מאוחסן בפועל במקום כלשהו, וכללי ה-CSS נמצאים בזיכרון הדפדפן במבני הנתונים של CSSOM.

איך הוספנו תמיכה ב-CSS-in-JS

לכן, כדי לתמוך בעריכה של כללי CSS-in-JS, החלטנו שהפתרון הטוב ביותר הוא ליצור מקור לגיליונות סגנונות מובנים שניתן לערוך באמצעות המנגנון הקיים שמתואר למעלה.

השלב הראשון הוא יצירת טקסט המקור. מנוע הסגנון של הדפדפן מאחסן את כללי ה-CSS במחלקה CSSStyleSheet. המחלקה הזו היא זו שאת המופעים שלה אפשר ליצור מ-JavaScript, כפי שהסברנו קודם. זהו הקוד לבניית טקסט המקור:

String InspectorStyleSheet::CollectStyleSheetRules() {
  StringBuilder builder;
  for (unsigned i = 0; i < page_style_sheet_->length(); i++) {
    builder.Append(page_style_sheet_->item(i)->cssText());
    builder.Append('\n');
  }
  return builder.ToString();
}

הוא חוזר על הכללים שנמצאים במופע CSSStyleSheet ויוצר ממנו מחרוזת אחת. שיטה זו מופעלת כאשר נוצר מופע של מחלקה InspectorStyleSheet. המחלקה InspectorStyleSheet מקיף מופע של CSSStyleSheet ומחלצת מטא נתונים נוספים הדרושים על ידי כלי הפיתוח:

void InspectorStyleSheet::UpdateText() {
  String text;
  bool success = InspectorStyleSheetText(&text);
  if (!success)
    success = InlineStyleSheetText(&text);
  if (!success)
    success = ResourceStyleSheetText(&text);
  if (!success)
    success = CSSOMStyleSheetText(&text);
  if (success)
    InnerSetText(text, false);
}

בקטע הקוד הזה אנחנו רואים CSSOMStyleSheetText שקורא ל-CollectStyleSheetRules באופן פנימי. CSSOMStyleSheetText מופעל אם גיליון הסגנונות אינו בתוך השורה או אם גיליון סגנונות של משאב. בעיקרון, שני קטעי הקוד האלה כבר מאפשרים עריכה בסיסית של גיליונות הסגנונות שנוצרו באמצעות הבנאי new CSSStyleSheet().

מקרה מיוחד הוא גיליונות הסגנונות המשויכים לתג <style> שעברו מוטציה באמצעות CSSOM API. במקרה כזה, גיליון הסגנונות מכיל את טקסט המקור וכללים נוספים שאינם נמצאים במקור. כדי לטפל במקרה הזה, אנחנו משיקים שיטה למיזוג הכללים הנוספים האלה בטקסט המקור. כאן, הסדר חשוב מפני שניתן להוסיף את כללי ה-CSS באמצע טקסט המקור המקורי. לדוגמה, נניח שהרכיב המקורי <style> מכיל את הטקסט הבא:

/* comment */
.rule1 {}
.rule3 {}

לאחר מכן הדף הוסיף כמה כללים חדשים באמצעות ה-API של JS, ויצר את סדר הכללים הבא: .rule0, .rule1, .rule2, .rule3, .rule4. לאחר פעולת המיזוג, טקסט המקור שיתקבל לאחר פעולת המיזוג צריך להיות:

.rule0 {}
/* comment */
.rule1 {}
.rule2 {}
.rule3 {}
.rule4 {}

שימור התגובות המקוריות וההרחקה מהשוליים חשובים לתהליך העריכה, מפני שמיקומי טקסט המקור של הכללים צריכים להיות מדויקים.

היבט נוסף שייחודי לגיליונות סגנונות של CSS-in-JS הוא שהדף יכול לשנות אותם בכל עת. אם כללי ה-CSSOM בפועל לא יסתנכרנו עם גרסת הטקסט, העריכה לא תפעל. לשם כך השקנו probe (בדיקה) שמאפשר לדפדפן להודיע לחלק העורפי של כלי הפיתוח לאחר שינוי של גיליון סגנונות. לאחר מכן, גיליונות סגנונות שעברו שינוי יסונכרנו במהלך הקריאה הבאה אל CSS.getMatchStylesForNode.

כשכל החלקים האלה קיימים, עריכת CSS-in-JS כבר פועלת, אבל רצינו לשפר את ממשק המשתמש ולציין אם נבנה גיליון סגנונות. הוספנו מאפיין חדש שנקרא isConstructed ל-CSS.CSSStyleSheetHeader של CDP. המאפיין הזה משמש את ממשק הקצה כדי להציג באופן תקין את המקור של כלל CSS:

גיליון סגנונות הניתן לבנייה

מסקנות

לסיכום הסיפור שלנו כאן, עברנו על התרחישים הרלוונטיים לדוגמה שקשורים ל-CSS-in-JS, שכלי הפיתוח לא תמכו בו, והסברנו את הפתרון לתמיכה בתרחישים האלה. החלק המעניין בהטמעה הזו הוא שהצלחנו למנף את הפונקציונליות הקיימת באמצעות יצירת טקסט מקור קבוע לכללי CSSOM, וכך נמנעה לחלוטין האדריכלי מחדש של עריכת הסגנון בכלי הפיתוח.

אם רוצים לקבל רקע נוסף, אפשר לעיין בהצעת העיצוב שלנו או בבאג המעקב ב-Chromium, שכולל התייחסות לכל התיקונים הקשורים.

מורידים את הערוצים של התצוגה המקדימה.

כדאי להשתמש ב-Chrome Canary, Dev או בטא כדפדפן הפיתוח שמוגדר כברירת מחדל. ערוצי התצוגה המקדימה האלה מעניקים לך גישה לתכונות החדשות של כלי הפיתוח, בודקים ממשקי API מתקדמים של פלטפורמת האינטרנט ומוצאים בעיות באתר לפני שהמשתמשים נתקלים בבעיות!

יצירת קשר עם צוות כלי הפיתוח ל-Chrome

אפשר להשתמש באפשרויות הבאות כדי לדון בתכונות ובשינויים החדשים בפוסט, או בכל דבר אחר שקשור לכלי הפיתוח.

  • שלחו לנו הצעה או משוב בכתובת crbug.com.
  • כדי לדווח על בעיה בכלי הפיתוח, לוחצים על אפשרויות נוספות   עוד   > עזרה > דיווח על בעיות בכלי הפיתוח בכלי הפיתוח.
  • אפשר לשלוח ציוץ אל @ChromeDevTools.
  • אפשר לכתוב תגובות לגבי 'מה חדש' בסרטוני YouTube או בקטע 'טיפים לשימוש בכלי הפיתוח' בסרטוני YouTube.