การรองรับ 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 ใน 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 ในเครื่องมือสำหรับนักพัฒนาเว็บ

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

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

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

บทสรุป

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

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

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

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

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

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