การรองรับ 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

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