การรองรับ CSS-in-JS ในเครื่องมือสำหรับนักพัฒนาเว็บ

Alex Rudenko
Alex Rudenko

บทความนี้กล่าวถึงการรองรับ CSS-in-JS ในเครื่องมือสำหรับนักพัฒนาเว็บที่เข้ามาตั้งแต่ Chrome 85 และโดยทั่วไปความหมายของ CSS-in-JS และสิ่งที่แตกต่างจาก CSS ทั่วไปที่ได้รับการสนับสนุนจากเครื่องมือสำหรับนักพัฒนาเว็บมาเป็นเวลานาน

CSS-in-JS คืออะไร

คำจำกัดความของ CSS-in-JS ค่อนข้างคลุมเครือ ในความหมายกว้างๆ นี่คือแนวทางในการจัดการโค้ด CSS โดยใช้ JavaScript ตัวอย่างเช่น อาจหมายความว่าเนื้อหา CSS ได้รับการกำหนดโดยใช้ JavaScript และแอปจะสร้างเอาต์พุต CSS สุดท้ายแบบเรียลไทม์

ในบริบทของเครื่องมือสำหรับนักพัฒนาเว็บ นั้น 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 ขั้นสูง โดยขึ้นอยู่กับโหมดของการพัฒนาและเบราว์เซอร์

มาดูตัวอย่างวิธีแทรกสไตล์ชีตโดยใช้ 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 ฟีเจอร์ที่ใช้กันมากที่สุดเมื่อทำงานกับ CSS คือแผงรูปแบบ ในแผงรูปแบบ คุณสามารถดูกฎที่ใช้กับองค์ประกอบหนึ่งๆ รวมถึงแก้ไขกฎและดูการเปลี่ยนแปลงในหน้าเว็บแบบเรียลไทม์ได้

ก่อนปีที่แล้ว การรองรับกฎ CSS ที่แก้ไขโดยใช้ CSSOM API นั้นค่อนข้างถูกจำกัด นั่นคือคุณจะเห็นเฉพาะกฎที่ใช้แล้วแต่แก้ไขไม่ได้ เป้าหมายหลักของเราเมื่อปีที่แล้วคือการอนุญาตให้แก้ไขกฎ CSS-in-JS โดยใช้แผงรูปแบบ บางครั้งเรายังเรียกรูปแบบ CSS-in-JS ว่า "constructed" เพื่อระบุว่ารูปแบบดังกล่าวสร้างขึ้นโดยใช้ Web API

มาเจาะลึกรายละเอียดของการแก้ไขรูปแบบในเครื่องมือสำหรับนักพัฒนาเว็บกัน

กลไกการแก้ไขรูปแบบในเครื่องมือสำหรับนักพัฒนาเว็บ

กลไกการแก้ไขรูปแบบในเครื่องมือสำหรับนักพัฒนาเว็บ

เมื่อเลือกองค์ประกอบในเครื่องมือสำหรับนักพัฒนาเว็บ แผงรูปแบบจะปรากฏขึ้น แผงรูปแบบจะออกคำสั่ง CDP ชื่อ CSS.getMatchedStylesForNode เพื่อรับกฎ CSS ที่ใช้กับองค์ประกอบ CDP ย่อมาจาก Chrome DevTools Protocol และเป็น API ที่ช่วยให้ฟรอนท์เอนด์ของเครื่องมือสำหรับนักพัฒนาเว็บได้รับข้อมูลเพิ่มเติมเกี่ยวกับหน้าที่ตรวจสอบ

เมื่อเรียกใช้ CSS.getMatchedStylesForNode จะระบุสไตล์ชีตทั้งหมดในเอกสารและแยกวิเคราะห์โดยใช้โปรแกรมแยกวิเคราะห์ CSS ของเบราว์เซอร์ จากนั้นจะสร้างดัชนีที่เชื่อมโยงกฎ CSS ทั้งหมดกับตำแหน่งในแหล่งที่มาของสไตล์ชีต

คุณอาจถามว่าเหตุใดจึงต้องแยกวิเคราะห์ CSS อีกครั้ง ปัญหาก็คือ เนื่องจากเหตุผลด้านประสิทธิภาพ เบราว์เซอร์เองไม่กังวลเกี่ยวกับตำแหน่งแหล่งที่มาของกฎ CSS จึงไม่จัดเก็บกฎเหล่านั้นไว้ แต่เครื่องมือสำหรับนักพัฒนาเว็บต้องใช้ตำแหน่งต้นทางเพื่อรองรับการแก้ไข CSS เราไม่ต้องการให้ผู้ใช้ Chrome ทั่วไปจ่ายโทษต่อประสิทธิภาพการทำงาน แต่เราต้องการให้ผู้ใช้เครื่องมือสำหรับนักพัฒนาเว็บสามารถเข้าถึงตําแหน่งต้นทางได้ แนวทางการแยกวิเคราะห์ใหม่นี้จะช่วยแก้ปัญหาทั้ง 2 กรณีโดยมีข้อเสียน้อยที่สุด

จากนั้น การใช้งาน 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 และดึงข้อมูลเมตาเพิ่มเติมที่เครื่องมือสำหรับ 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 ซึ่งช่วยให้เบราว์เซอร์แจ้งเตือนส่วนแบ็กเอนด์ของเครื่องมือสำหรับนักพัฒนาเว็บเมื่อมีการเปลี่ยนรูปแบบสไตล์ชีต จากนั้น ระบบจะซิงค์สไตล์ชีตที่เปลี่ยนแปลงในระหว่างการเรียก CSS.getMatchStylesForNode ครั้งถัดไป

เมื่อมีส่วนประกอบทั้งหมดเหล่านี้แล้ว การแก้ไข CSS-in-JS ก็ใช้ได้อยู่แล้ว แต่เราต้องการปรับปรุง UI เพื่อระบุว่ามีการสร้างสไตล์ชีตหรือไม่ เราได้เพิ่มแอตทริบิวต์ใหม่ที่ชื่อว่า isConstructed ไปยัง CSS.CSSStyleSheetHeader ของ CDP ซึ่งฟรอนท์เอนด์ใช้เพื่อแสดงแหล่งที่มาของกฎ CSS อย่างถูกต้องดังนี้

สไตล์ชีตที่สร้างได้

บทสรุป

เพื่อสรุปเรื่องราวของเราในที่นี้ เราได้ดู Use Case ที่เกี่ยวข้องซึ่งเกี่ยวข้องกับ CSS-in-JS ที่เครื่องมือสำหรับนักพัฒนาเว็บไม่รองรับและอธิบายโซลูชันเพื่อสนับสนุนกรณีการใช้งานเหล่านั้นโดยละเอียด สิ่งที่น่าสนใจของการนำเครื่องมือนี้ไปใช้คือเราสามารถใช้ประโยชน์จากฟังก์ชันที่มีอยู่โดยทำให้กฎ CSSOM CSS มีข้อความต้นฉบับที่สม่ำเสมอ ซึ่งช่วยให้ไม่ต้องแก้ไขสไตล์ทั้งหมดในเครื่องมือสำหรับนักพัฒนาเว็บอีกต่อไป

หากต้องการทราบข้อมูลพื้นฐานเพิ่มเติม โปรดดูข้อเสนอการออกแบบของเราหรือข้อบกพร่องในการติดตามของ Chromium ซึ่งอ้างอิงแพตช์ที่เกี่ยวข้องทั้งหมด

ดาวน์โหลดเวอร์ชันตัวอย่าง

ลองใช้ Chrome Canary, Dev หรือ เบต้า เป็นเบราว์เซอร์เริ่มต้นสำหรับการพัฒนา ช่องทางพรีวิวเหล่านี้จะทำให้คุณเข้าถึงฟีเจอร์ล่าสุดของเครื่องมือสำหรับนักพัฒนาเว็บ ทดสอบ API แพลตฟอร์มเว็บที่ล้ำสมัย และพบปัญหาในเว็บไซต์ก่อนผู้ใช้

ติดต่อทีม Chrome DevTools

ใช้ตัวเลือกต่อไปนี้เพื่อพูดคุยเกี่ยวกับฟีเจอร์ใหม่และการเปลี่ยนแปลงในโพสต์ หรืออื่นๆ ที่เกี่ยวข้องกับเครื่องมือสำหรับนักพัฒนาเว็บ

  • ส่งข้อเสนอแนะหรือความคิดเห็นถึงเราทาง crbug.com
  • รายงานปัญหาเกี่ยวกับเครื่องมือสำหรับนักพัฒนาเว็บโดยใช้ตัวเลือกเพิ่มเติม   เพิ่มเติม   > ความช่วยเหลือ > รายงานปัญหาเกี่ยวกับเครื่องมือสำหรับนักพัฒนาเว็บในเครื่องมือสำหรับนักพัฒนาเว็บ
  • ทวีตที่ @ChromeDevTools
  • แสดงความคิดเห็นว่ามีอะไรใหม่ในวิดีโอ YouTube เครื่องมือสำหรับนักพัฒนาเว็บ หรือวิดีโอ YouTube สำหรับเครื่องมือสำหรับนักพัฒนาเว็บ