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

Alex Rudenko
Alex Rudenko

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

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

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

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

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

ตอนนี้เรามาดูการแก้ไขกัน โปรดทราบว่า CSS.getMatchedStylesForNode จะแสดงตําแหน่งแหล่งที่มาของกฎทุกข้อ ซึ่งเป็นสิ่งที่สําคัญอย่างยิ่งในการแก้ไข เมื่อคุณเปลี่ยนกฎ เครื่องมือสําหรับนักพัฒนาเว็บจะออกคําสั่ง CDP อื่นที่จะอัปเดตหน้าเว็บจริงๆ คำสั่งนี้ประกอบด้วยตำแหน่งเดิมของส่วนของกฎที่อัปเดตอยู่และข้อความใหม่ที่ต้องอัปเดตส่วนของกฎ

ในแบ็กเอนด์ เมื่อจัดการการเรียกให้แก้ไข เครื่องมือสำหรับนักพัฒนาเว็บจะอัปเดตสไตล์ชีตเป้าหมาย รวมถึงจะอัปเดตสําเนาแหล่งที่มาของสไตล์ชีตที่ดูแลรักษาอยู่ และอัปเดตตําแหน่งแหล่งที่มาของกฎที่อัปเดต ในการตอบสนองต่อการเรียกใช้การแก้ไข อินเทอร์เฟซส่วนหน้าของ DevTools จะได้รับตําแหน่งที่อัปเดตแล้วของข้อความที่เพิ่งอัปเดต

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

วิธีที่เพิ่มการรองรับ CSS ใน 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 หากสไตล์ชีตไม่ได้อยู่ในรูปแบบอินไลน์หรือเป็นสไตล์ชีตทรัพยากร โดยพื้นฐานแล้ว ข้อมูลโค้ด 2 รายการนี้อนุญาตให้แก้ไขสไตล์ชีตพื้นฐานที่สร้างโดยใช้คอนสตรัคเตอร์ new CSSStyleSheet() อยู่แล้ว

กรณีพิเศษคือสไตล์ชีตที่เชื่อมโยงกับแท็ก <style> ซึ่งได้รับการเปลี่ยนรูปแบบโดยใช้ CSSOM API ในกรณีนี้ สไตล์ชีตจะมีข้อความต้นทางและกฎเพิ่มเติมที่ไม่ได้อยู่ในแหล่งที่มา ในการรับมือกับกรณีนี้ เราขอแนะนำวิธีผสานกฎเพิ่มเติมเหล่านั้นลงในข้อความต้นฉบับ ลำดับของกฎ CSS มีความสำคัญเนื่องจากสามารถแทรกกฎ CSS ไว้ตรงกลางข้อความต้นฉบับได้ ตัวอย่างเช่น สมมติว่าองค์ประกอบ <style> เดิมมีข้อความดังต่อไปนี้

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

จากนั้นหน้าเว็บแทรกกฎใหม่โดยใช้ JS API ซึ่งสร้างลําดับกฎดังนี้ .rule0, .rule1, .rule2, .rule3, .rule4 ข้อความต้นทางที่ได้หลังจากการดำเนินการผสานควรมีลักษณะดังนี้

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

การคงความคิดเห็นเดิมและการเยื้องเป็นสิ่งสำคัญสำหรับกระบวนการแก้ไข เนื่องจากตําแหน่งข้อความต้นทางของกฎต้องถูกต้อง

อีกแง่มุมหนึ่งที่พิเศษสำหรับสไตล์ชีต CSS-in-JS คือหน้าเว็บสามารถเปลี่ยนแปลงสไตล์ชีตได้ทุกเมื่อ หากกฎ CSSOM จริงไม่ซิงค์กับเวอร์ชันข้อความ การแก้ไขจะไม่ทำงาน ด้วยเหตุนี้ เราจึงเปิดตัวสิ่งที่เรียกว่า โปรบ ซึ่งช่วยให้เบราว์เซอร์แจ้งให้ส่วนแบ็กเอนด์ของ DevTools ทราบเมื่อมีการปรับเปลี่ยนสไตล์ชีต จากนั้นระบบจะซิงค์สไตล์ชีตที่เปลี่ยนแปลงไปในระหว่างการเรียก CSS.getMatchedStylesForNode ครั้งถัดไป

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

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

สรุป

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

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

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

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

ติดต่อทีมเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome

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