ปรับปรุงโครงสร้างพื้นฐาน CSS ให้ทันสมัยในเครื่องมือสำหรับนักพัฒนาเว็บ

รีเฟรชสถาปัตยกรรมเครื่องมือสำหรับนักพัฒนาเว็บ: การปรับโครงสร้างพื้นฐาน CSS ให้ทันสมัยในเครื่องมือสำหรับนักพัฒนาเว็บ

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

สถานะก่อนหน้าของ CSS ในเครื่องมือสำหรับนักพัฒนาเว็บ

DevTools ใช้ CSS ใน 2 วิธีด้วยกัน วิธีแรกสำหรับไฟล์ CSS ที่ใช้ในส่วนเดิมของ DevTools และอีกวิธีสำหรับคอมโพเนนต์เว็บสมัยใหม่ที่ใช้ใน DevTools

การใช้งาน CSS ในเครื่องมือสำหรับนักพัฒนาเว็บได้รับการกำหนดไว้เมื่อหลายปีก่อนและล้าสมัยแล้ว เครื่องมือสำหรับนักพัฒนาเว็บยังคงใช้รูปแบบ module.json และเราพยายามอย่างมากในการนําไฟล์เหล่านี้ออก ตัวบล็อกสุดท้ายที่ป้องกันไม่ให้นำไฟล์เหล่านี้ออกคือส่วน resources ซึ่งใช้โหลดไฟล์ CSS

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

ไฟล์ CSS ที่อยู่ในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์จะถือว่า "เดิม" เนื่องจากระบบโหลดไฟล์เหล่านั้นโดยใช้ไฟล์ module.json ซึ่งกําลังอยู่ในขั้นตอนการนําออก ไฟล์ CSS ทั้งหมดต้องแสดงอยู่ใน resources ในไฟล์ module.json ในไดเรกทอรีเดียวกับไฟล์ CSS

ตัวอย่างไฟล์ module.json ที่เหลือ

{
  "resources": [
    "serviceWorkersView.css",
    "serviceWorkerUpdateCycleView.css"
  ]
}

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

ตัวอย่าง registerRequiredCSS call

constructor() {
  
  this.registerRequiredCSS('ui/legacy/components/quick_open/filteredListWidget.css');
  
}

ซึ่งจะดึงข้อมูลในไฟล์ CSS และแทรกเป็นองค์ประกอบ <style> ลงในหน้าเว็บโดยใช้ฟังก์ชัน appendStyle

ฟังก์ชัน appendStyle ที่เพิ่ม CSS โดยใช้องค์ประกอบสไตล์แบบอินไลน์

const content = Root.Runtime.cachedResources.get(cssFile) || '';

if (!content) {
  console.error(cssFile + ' not preloaded. Check module.json');
}

const styleElement = document.createElement('style');
styleElement.textContent = content;
node.appendChild(styleElement);

เมื่อเราเปิดตัวคอมโพเนนต์เว็บสมัยใหม่ (โดยใช้องค์ประกอบที่กำหนดเอง) เราตัดสินใจในตอนแรกว่าจะใช้ CSS ผ่านแท็ก <style> ในบรรทัดในไฟล์คอมโพเนนต์เอง ซึ่งก็ทำให้เกิดปัญหาตามมา

  • ไม่รองรับการไฮไลต์ไวยากรณ์ ปลั๊กอินที่ไฮไลต์ไวยากรณ์สำหรับ CSS ในบรรทัดมักจะมีประสิทธิภาพไม่เท่ากับฟีเจอร์ไฮไลต์ไวยากรณ์และการเติมข้อความอัตโนมัติสำหรับ CSS ที่เขียนในไฟล์ .css
  • สร้างค่าใช้จ่ายเพิ่มเติมด้านประสิทธิภาพ นอกจากนี้ CSS ในบรรทัดยังหมายความว่าจะต้องทำการแก้ไข 2 ครั้ง ได้แก่ 1 ครั้งสำหรับไฟล์ CSS และ 1 ครั้งสำหรับ CSS ในบรรทัด นี่เป็นค่าใช้จ่ายด้านประสิทธิภาพที่เรานำออกได้หาก CSS ทั้งหมดเขียนในไฟล์ CSS แบบสแตนด์อโลน
  • ปัญหาในการย่อขนาด CSS ในหน้านั้นสามารถลดขนาดลงได้ยาก ดังนั้นจึงไม่มีการลด CSS ใดเลย นอกจากนี้ ขนาดไฟล์ของบิลด์รุ่นของ DevTools ยังเพิ่มขึ้นด้วย CSS ที่ซ้ำกันซึ่งเกิดจากอินสแตนซ์หลายรายการของคอมโพเนนต์เว็บเดียวกัน

เป้าหมายของโปรเจ็กต์การฝึกงานของฉันคือการหาโซลูชันสำหรับโครงสร้างพื้นฐาน CSS ที่ทำงานได้กับทั้งโครงสร้างพื้นฐานเดิมและคอมโพเนนต์เว็บใหม่ที่ใช้ใน DevTools

ค้นหาวิธีแก้ปัญหาที่เป็นไปได้

ปัญหานี้อาจแบ่งออกเป็น 2 ส่วนดังนี้

  • ดูว่าระบบบิลด์จัดการไฟล์ CSS อย่างไร
  • แสดงวิธีการนำเข้าและใช้งานไฟล์ CSS ด้วยเครื่องมือสำหรับนักพัฒนาเว็บ

เราได้พิจารณาวิธีแก้ปัญหาที่เป็นไปได้ต่างๆ สำหรับแต่ละส่วนไว้ด้านล่าง

การนําเข้าไฟล์ CSS

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

ด้วยเหตุนี้ คำสั่ง @import และแท็ก จึงดูไม่เหมาะสําหรับเครื่องมือสําหรับนักพัฒนาซอฟต์แวร์ เนื่องจากจะไม่มีการนำเข้าแบบเดียวกันกับส่วนที่เหลือของเครื่องมือสำหรับนักพัฒนาเว็บ และส่งผลให้เกิดเนื้อหาที่แสดงขึ้นมาโดยไม่มีการจัดรูปแบบ (FOUC) การย้ายข้อมูลไปยังสคริปต์โมดูล CSS จะทำได้ยากขึ้น เนื่องจากจะต้องมีการเพิ่มและจัดการกับการนำเข้าอย่างชัดเจนและแตกต่างจากการนำเข้าด้วยแท็ก <link>

const output = LitHtml.html`
<style> @import "css/styles.css"; </style>
<button> Hello world </button>`
const output = LitHtml.html`
<link rel="stylesheet" href="styles.css">
<button> Hello World </button>`

ทางแก้ปัญหาที่เป็นไปได้โดยใช้ @import หรือ <link>

แต่เราเลือกที่จะหาวิธีนำเข้าไฟล์ CSS เป็นออบเจ็กต์ CSSStyleSheet เพื่อให้เราสามารถเพิ่มไฟล์ไปยัง Shadow Dom (DevTools ใช้ Shadow DOM มาเป็นเวลา 2-3 ปีแล้ว) โดยใช้พร็อพเพอร์ตี้ adoptedStyleSheets

ตัวเลือก Bundler

เราต้องการวิธีแปลงไฟล์ CSS เป็นออบเจ็กต์ CSSStyleSheet เพื่อให้จัดการไฟล์ TypeScript ได้ง่าย เราพิจารณาทั้ง Rollup และ webpack เป็น Bundler ที่อาจทำการเปลี่ยนแปลงนี้ให้เราได้ DevTools ใช้ Rollup ในบิลด์สำหรับใช้งานจริงอยู่แล้ว แต่การเพิ่มเครื่องมือจัดกลุ่มลงในบิลด์สำหรับใช้งานจริงอาจทำให้เกิดปัญหาด้านประสิทธิภาพเมื่อทำงานร่วมกับระบบบิลด์ปัจจุบันของเรา การผสานรวมกับระบบบิลด์ GN ของ Chromium ทำให้การรวมกลุ่มทำได้ยากขึ้น และเครื่องมือรวมกลุ่มจึงมีแนวโน้มที่จะผสานรวมกับระบบบิลด์ Chromium ปัจจุบันได้ไม่ดี

แต่เราเลือกที่จะใช้ระบบการบิลด์ GN ปัจจุบันเพื่อเปลี่ยนรูปแบบให้เราแทน

โครงสร้างพื้นฐานใหม่ในการใช้ CSS ในเครื่องมือสำหรับนักพัฒนาเว็บ

โซลูชันใหม่นี้เกี่ยวข้องกับการใช้ adoptedStyleSheets เพื่อเพิ่มสไตล์ลงใน Shadow DOM บางรายการขณะใช้ระบบการบิลด์ GN เพื่อสร้างออบเจ็กต์ CSSStyleSheet ที่ document หรือ ShadowRoot นำมาใช้ได้

// CustomButton.ts

// Import the CSS style sheet contents from a JS file generated from CSS
import customButtonStyles from './customButton.css.js';
import otherStyles from './otherStyles.css.js';

export class CustomButton extends HTMLElement{
  
  connectedCallback(): void {
    // Add the styles to the shadow root scope
    this.shadow.adoptedStyleSheets = [customButtonStyles, otherStyles];
  }
}

การใช้ adoptedStyleSheets มีประโยชน์หลายประการ ดังนี้

  • ซึ่งกำลังอยู่ระหว่างการพัฒนาให้เป็นมาตรฐานเว็บสมัยใหม่
  • ป้องกัน CSS ที่ซ้ำกัน
  • ใช้สไตล์กับ Shadow DOM เท่านั้น ซึ่งจะช่วยหลีกเลี่ยงปัญหาที่เกิดจากชื่อคลาสหรือตัวเลือกรหัสที่ซ้ำกันในไฟล์ CSS
  • ย้ายข้อมูลไปยังมาตรฐานเว็บในอนาคตได้อย่างง่ายดาย เช่น สคริปต์โมดูล CSS และการยืนยันการนำเข้า

ข้อควรระวังเพียงอย่างเดียวของวิธีนี้คือคำสั่ง import กำหนดให้ต้องนำเข้าไฟล์ .css.js เราได้เขียนสคริปต์ generate_css_js_files.js เพื่อให้ GN สร้างไฟล์ CSS ในระหว่างการสร้าง ตอนนี้ระบบในรุ่นจะประมวลผลไฟล์ CSS ทั้งหมดและเปลี่ยนรูปแบบเป็นไฟล์ JavaScript ที่ส่งออกออบเจ็กต์ CSSStyleSheet โดยค่าเริ่มต้น นี่เป็นวิธีที่ยอดเยี่ยมเนื่องจากเราสามารถนําเข้าไฟล์ CSS และใช้ได้อย่างง่ายดาย นอกจากนี้ เรายังลดขนาดรุ่นที่ใช้งานจริงได้อย่างง่ายดายเพื่อประหยัดขนาดไฟล์ได้ด้วย โดยทำดังนี้

const styles = new CSSStyleSheet();
styles.replaceSync(
  // In production, we also minify our CSS styles
  /`${isDebug ? output : cleanCSS.minify(output).styles}
  /*# sourceURL=${fileName} */`/
);

export default styles;

ตัวอย่าง iconButton.css.js ที่สร้างขึ้นจากสคริปต์

การย้ายข้อมูลโค้ดเดิมโดยใช้กฎ ESLint

แม้ว่าการย้ายข้อมูลคอมโพเนนต์เว็บด้วยตนเองจะทําได้ง่ายๆ แต่กระบวนการย้ายข้อมูลการใช้งานเดิมของ registerRequiredCSS นั้นซับซ้อนกว่า ฟังก์ชันหลัก 2 รายการที่ลงทะเบียนสไตล์เดิมคือ registerRequiredCSS และ createShadowRootWithCoreStyles เราตัดสินใจว่าเนื่องจากขั้นตอนการย้ายข้อมูลการเรียกเหล่านี้ค่อนข้างมีความสำคัญ เราใช้กฎ ESLint เพื่อทำการแก้ไขและย้ายโค้ดเดิมได้โดยอัตโนมัติ เครื่องมือสำหรับนักพัฒนาเว็บใช้กฎที่กำหนดเองจำนวนหนึ่งอยู่แล้วสำหรับฐานของโค้ดเครื่องมือสำหรับนักพัฒนาเว็บ ซึ่งมีประโยชน์เนื่องจาก ESLint จะแยกวิเคราะห์โค้ดเป็นAbstract Syntax Tree (ย่อมาจาก AST) AST) และเราค้นหาโหนดการเรียกที่เฉพาะเจาะจงซึ่งเรียก CSS ที่ลงทะเบียนได้

ปัญหาใหญ่ที่สุดที่เราพบเมื่อเขียนกฎ ESLint สำหรับการย้ายข้อมูลคือการจับขอบเขตกรณี เราต้องการหาจุดสมดุลที่เหมาะสมระหว่างการรู้ว่ากรณีขอบใดควรบันทึกไว้และกรณีขอบใดควรย้ายข้อมูลด้วยตนเอง นอกจากนี้ เรายังต้องสามารถแจ้งให้ผู้ใช้ทราบได้เมื่อระบบสร้างไฟล์ .css.js ที่นำเข้าโดยอัตโนมัติไม่ได้ เนื่องจากการดำเนินการนี้จะช่วยป้องกันข้อผิดพลาดที่ระบบไม่พบไฟล์รันไทม์

ข้อเสียอย่างหนึ่งของการใช้กฎ ESLint สำหรับการย้ายข้อมูลคือเราไม่สามารถเปลี่ยนแปลงไฟล์บิลด์ GN ที่จำเป็นในระบบได้ ผู้ใช้ต้องทําการเปลี่ยนแปลงเหล่านี้ด้วยตนเองในแต่ละไดเรกทอรี แม้ว่าการดำเนินการนี้จะต้องดำเนินการเพิ่มเติม แต่ก็เป็นวิธีที่ดีในการยืนยันว่าระบบบิลด์เป็นผู้สร้างไฟล์ .css.js ทุกๆ ไฟล์ที่นำเข้าจริงๆ

โดยรวมแล้ว การใช้กฎ ESLint สำหรับการย้ายข้อมูลนี้มีประโยชน์มาก เนื่องจากเราย้ายข้อมูลโค้ดเดิมไปยังโครงสร้างพื้นฐานใหม่ได้อย่างรวดเร็ว และการมี AST พร้อมใช้งานทำให้เราสามารถจัดการกับกรณีขอบหลายรายการในกฎและแก้ไขโดยอัตโนมัติได้อย่างน่าเชื่อถือโดยใช้ Fixer API ของ ESLint

มีอะไรต่อไป

จนถึงตอนนี้ คอมโพเนนต์เว็บทั้งหมดในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ Chromium ได้ย้ายข้อมูลไปใช้โครงสร้างพื้นฐาน CSS ใหม่แทนการใช้รูปแบบอินไลน์แล้ว นอกจากนี้ เรายังได้ย้ายข้อมูลการใช้งานเดิมส่วนใหญ่ของ registerRequiredCSS ไปใช้ระบบใหม่ด้วย สิ่งที่เหลืออยู่คือนำไฟล์ module.json ออกให้มากที่สุด แล้วย้ายข้อมูลโครงสร้างพื้นฐานปัจจุบันนี้เพื่อใช้สคริปต์โมดูล CSS ในอนาคต

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

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

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

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