پشتیبانی از CSS-in-JS در DevTools

الکس رودنکو
Alex Rudenko

این مقاله در مورد پشتیبانی از CSS-in-JS در DevTools صحبت می کند که از Chrome 85 فرود آمد و، به طور کلی، منظور ما از CSS-in-JS و تفاوت آن با CSS معمولی که برای مدت طولانی توسط DevTools پشتیبانی می شد، چیست.

CSS-in-JS چیست؟

تعریف CSS-in-JS نسبتا مبهم است. در یک مفهوم گسترده، این یک رویکرد برای مدیریت کد CSS با استفاده از جاوا اسکریپت است. به عنوان مثال، ممکن است به این معنی باشد که محتوای CSS با استفاده از جاوا اسکریپت تعریف شده است و خروجی نهایی CSS در همان لحظه توسط برنامه تولید می شود.

در زمینه DevTools، CSS-in-JS به این معنی است که محتوای CSS با استفاده از CSSOM API به صفحه تزریق می‌شود. CSS معمولی با استفاده از عناصر <style> یا <link> تزریق می شود و دارای یک منبع ثابت (مثلاً یک گره DOM یا یک منبع شبکه) است. در مقابل، CSS-in-JS اغلب منبع ثابتی ندارد. یک مورد خاص در اینجا این است که محتوای عنصر <style> را می توان با استفاده از CSSOM API به روز کرد و باعث می شود منبع با صفحه سبک CSS واقعی هماهنگ نشود.

اگر از هر کتابخانه CSS-in-JS استفاده می‌کنید (به عنوان مثال styled-component ، Emotion ، JSS )، کتابخانه ممکن است بسته به حالت توسعه و مرورگر، سبک‌ها را با استفاده از CSSOM APIها در زیر سرپوش تزریق کند.

بیایید به چند مثال نگاه کنیم که چگونه می‌توانید یک stylesheet را با استفاده از 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 در DevTools

در DevTools، متداول‌ترین ویژگی مورد استفاده در هنگام کار با CSS، پنجره Styles است. در بخش Styles ، می‌توانید قوانین مربوط به یک عنصر خاص را مشاهده کنید و می‌توانید قوانین را ویرایش کنید و تغییرات صفحه را در زمان واقعی مشاهده کنید.

قبل از سال گذشته، پشتیبانی از قوانین CSS اصلاح شده با استفاده از CSSOM APIها نسبتاً محدود بود: شما فقط می توانستید قوانین اعمال شده را ببینید اما نمی توانید آنها را ویرایش کنید. هدف اصلی ما در سال گذشته اجازه ویرایش قوانین CSS-in-JS با استفاده از صفحه Styles بود. گاهی اوقات ما سبک‌های CSS-in-JS را «ساخت‌شده» می‌نامیم تا نشان دهیم که با استفاده از Web API ساخته شده‌اند.

بیایید به جزئیات کارهای ویرایش سبک در DevTools بپردازیم.

مکانیسم ویرایش سبک در DevTools

مکانیسم ویرایش سبک در DevTools

هنگامی که یک عنصر را در DevTools انتخاب می کنید، صفحه Styles نشان داده می شود. پنجره Styles یک دستور CDP به نام CSS.getMatchedStylesForNode را صادر می کند تا قوانین CSS را که بر روی عنصر اعمال می شود دریافت کند. CDP مخفف Chrome DevTools Protocol است و یک API است که به DevTools frontend اجازه می‌دهد اطلاعات بیشتری در مورد صفحه بازرسی شده دریافت کند.

هنگامی که فراخوانی می شود، CSS.getMatchedStylesForNode همه شیوه نامه ها را در سند شناسایی می کند و آنها را با استفاده از تجزیه کننده CSS مرورگر تجزیه می کند. سپس یک شاخص ایجاد می کند که هر قانون CSS را با یک موقعیت در منبع شیوه نامه مرتبط می کند.

ممکن است بپرسید، چرا باید دوباره CSS را تجزیه کند؟ مشکل اینجاست که به دلایل عملکرد، خود مرورگر به موقعیت‌های منبع قوانین CSS اهمیتی نمی‌دهد و بنابراین، آنها را ذخیره نمی‌کند. اما DevTools به موقعیت های منبع برای پشتیبانی از ویرایش CSS نیاز دارد. ما نمی‌خواهیم کاربران عادی Chrome جریمه عملکرد را بپردازند، اما می‌خواهیم کاربران DevTools به موقعیت‌های منبع دسترسی داشته باشند. این رویکرد تجزیه مجدد به هر دو مورد استفاده با حداقل جنبه های منفی می پردازد.

سپس، پیاده‌سازی CSS.getMatchedStylesForNode از موتور سبک مرورگر می‌خواهد تا قوانین CSS را ارائه کند که با عنصر داده‌شده مطابقت داشته باشد. و در نهایت، این روش قوانین بازگردانده شده توسط موتور سبک را با کد منبع مرتبط می کند و یک پاسخ ساختاریافته در مورد قوانین CSS ارائه می دهد تا DevTools بداند کدام قسمت از قانون انتخابگر یا ویژگی ها است. این به DevTools اجازه می دهد تا انتخابگر و ویژگی ها را به طور مستقل ویرایش کند.

حالا بیایید به ویرایش نگاه کنیم. به یاد داشته باشید که CSS.getMatchedStylesForNode موقعیت های منبع را برای هر قانون برمی گرداند؟ این برای ویرایش بسیار مهم است. هنگامی که یک قانون را تغییر می دهید، DevTools دستور CDP دیگری را صادر می کند که در واقع صفحه را به روز می کند. این دستور شامل موقعیت اصلی قطعه قانون است که در حال به روز رسانی است و متن جدیدی که قطعه باید با آن به روز شود.

در پشتیبان، هنگام رسیدگی به تماس ویرایش، DevTools شیوه نامه هدف را به روز می کند. همچنین کپی منبع شیوه نامه را که حفظ می کند به روز می کند و موقعیت های منبع را برای قانون به روز شده به روز می کند. در پاسخ به فراخوان ویرایش، بخش DevTools موقعیت‌های به‌روز شده را برای قطعه متنی که به‌تازگی به‌روزرسانی شده است، برمی‌گرداند.

این توضیح می‌دهد که چرا ویرایش CSS-in-JS در DevTools به درستی کار نمی‌کند: CSS-in-JS منبع واقعی ذخیره‌شده در جایی ندارد و قوانین CSS در حافظه مرورگر در ساختارهای داده CSSOM زنده هستند .

چگونه پشتیبانی از CSS-in-JS را اضافه کردیم

بنابراین، برای پشتیبانی از ویرایش قوانین CSS-in-JS، ما تصمیم گرفتیم که بهترین راه حل ایجاد منبعی برای شیوه نامه های ساخته شده است که می تواند با استفاده از مکانیزم موجود که در بالا توضیح داده شد، ویرایش شود.

اولین قدم ساختن متن منبع است. موتور سبک مرورگر قوانین CSS را در کلاس CSSStyleSheet ذخیره می کند. آن کلاس همان کلاسی است که می‌توانید نمونه‌های آن را از جاوا اسکریپت همانطور که قبلاً بحث شد ایجاد کنید. کد ساخت متن منبع به شرح زیر است:

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 را بسته بندی می کند و ابرداده های اضافی مورد نیاز DevTools را استخراج می کند:

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 {}

سپس صفحه با استفاده از JS API قوانین جدیدی را وارد کرد که ترتیب قوانین زیر را تولید می کند: .rule0، .rule1، .rule2، .rule3، .rule4. متن منبع حاصل پس از عملیات ادغام باید به صورت زیر باشد:

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

حفظ نظرات اصلی و تورفتگی برای فرآیند ویرایش مهم است زیرا موقعیت متن منبع قوانین باید دقیق باشد.

یکی دیگر از جنبه هایی که برای شیوه نامه های CSS-in-JS ویژه است این است که می توان آنها را در هر زمان توسط صفحه تغییر داد . اگر قوانین CSSOM واقعی با نسخه متنی هماهنگ نباشد، ویرایش کار نخواهد کرد. برای این کار ما به اصطلاح probe را معرفی کردیم که به مرورگر اجازه می‌دهد تا قسمت پشتیبان DevTools را هنگام جهش یک stylesheet مطلع کند. سپس شیوه نامه های تغییر یافته در طول تماس بعدی با CSS.getMatchedStylesForNode همگام می شوند.

با وجود همه این قطعات، ویرایش CSS-in-JS از قبل کار می کند، اما ما می خواستیم رابط کاربری را بهبود بخشیم تا نشان دهیم که آیا یک شیوه نامه ساخته شده است یا خیر. ما یک ویژگی جدید به نام isConstructed به CSS.CSSStyleSheetHeader CDP اضافه کرده‌ایم که frontend از آن برای نمایش صحیح منبع یک قانون CSS استفاده می‌کند:

شیوه نامه قابل ساخت

نتیجه گیری

برای خلاصه کردن داستان خود در اینجا، موارد استفاده مربوط به CSS-in-JS را که DevTools پشتیبانی نمی کرد بررسی کردیم و راه حلی را برای پشتیبانی از این موارد استفاده کردیم. بخش جالب این پیاده سازی این است که ما توانستیم با ایجاد یک متن منبع منظم قوانین CSSOM CSS از عملکردهای موجود استفاده کنیم و از نیاز به تغییر کامل ویرایش سبک در DevTools اجتناب کنیم.

برای پیشینه بیشتر، پیشنهاد طراحی ما یا اشکال ردیابی Chromium را بررسی کنید که به همه وصله‌های مرتبط ارجاع می‌دهد.

کانال های پیش نمایش را دانلود کنید

استفاده از Chrome Canary ، Dev یا Beta را به عنوان مرورگر توسعه پیش‌فرض خود در نظر بگیرید. این کانال‌های پیش‌نمایش به شما امکان می‌دهند به جدیدترین ویژگی‌های DevTools دسترسی داشته باشید، APIهای پلت‌فرم وب پیشرفته را آزمایش کنید و قبل از اینکه کاربرانتان این کار را انجام دهند، مشکلات موجود در سایت خود را پیدا کنید!

تماس با تیم Chrome DevTools

از گزینه های زیر برای بحث در مورد ویژگی ها و تغییرات جدید در پست یا هر چیز دیگری مربوط به DevTools استفاده کنید.

  • پیشنهاد یا بازخورد خود را از طریق crbug.com برای ما ارسال کنید.
  • با استفاده از گزینه های بیشتر ، مشکل DevTools را گزارش کنیدبیشتر > راهنما > گزارش مشکلات DevTools در DevTools.
  • توییت در @ChromeDevTools .
  • نظرات خود را در مورد ویدیوهای YouTube DevTools یا نکات DevTools در YouTube ما بنویسید.