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

อเล็กซ์ รูเดงโก
อเล็กซ์ รูเดงโก

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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