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

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

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

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

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

การติดตั้งใช้งาน 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 การโทร:

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

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

การวิจัยโซลูชันที่เป็นไปได้

โจทย์อาจแบ่งออกเป็น 2 ส่วน ดังนี้

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

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

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

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

ด้วยเหตุนี้ คำสั่ง @import และแท็ก จึงดูไม่เหมาะกับเครื่องมือสำหรับนักพัฒนาเว็บ การนำเข้าจะไม่เป็นแบบเดียวกันกับการนำเข้าอื่นๆ ทั้งหมดของเครื่องมือสำหรับนักพัฒนาเว็บ และส่งผลให้เกิด Flash Of Unstyled Content (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 เป็น Bundle ที่มีโอกาสดำเนินการเปลี่ยนรูปแบบนี้ให้เรา เครื่องมือสำหรับนักพัฒนาเว็บใช้ Rollup อยู่แล้วในบิลด์ที่ใช้งานจริง แต่การเพิ่ม Bundler ตัวใดตัวหนึ่งลงในบิลด์ที่ใช้งานจริงอาจมีปัญหาด้านประสิทธิภาพเมื่อทำงานร่วมกับระบบบิลด์ปัจจุบัน การผสานรวมของเรากับระบบบิลด์ GN ของ Chromium ทำให้การรวมกลุ่มทำได้ยากขึ้น ดังนั้น Bundler มักจะผสานรวมกับระบบบิลด์ 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(abbr. AST) แล้วเราสามารถค้นหาโหนดการเรียกที่เจาะจงซึ่งเป็นการเรียกไปยังการลงทะเบียน CSS

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

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

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

แล้วยังไงต่อดี

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

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

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

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

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

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