Hiện đại hoá cơ sở hạ tầng CSS trong Công cụ cho nhà phát triển

Làm mới cấu trúc Công cụ cho nhà phát triển: Hiện đại hoá cơ sở hạ tầng CSS trong Công cụ cho nhà phát triển

Bài đăng này nằm trong loạt bài đăng trên blog mô tả những thay đổi mà chúng tôi đang thực hiện đối với cấu trúc Công cụ cho nhà phát triển và cách xây dựng công cụ này. Chúng tôi sẽ giải thích cách CSS hoạt động trước đây trong Công cụ cho nhà phát triển và cách chúng tôi hiện đại hóa CSS trong Công cụ cho nhà phát triển để chuẩn bị (sau cùng) di chuyển sang giải pháp web chuẩn để tải CSS trong tệp JavaScript.

Trạng thái trước đó của CSS trong Công cụ cho nhà phát triển

Công cụ cho nhà phát triển đã triển khai CSS theo 2 cách: một cho các tệp CSS được dùng trong phần cũ của Công cụ cho nhà phát triển và một cách cho các thành phần web hiện đại đang được sử dụng trong Công cụ cho nhà phát triển.

Việc triển khai CSS trong Công cụ cho nhà phát triển đã được xác định từ nhiều năm trước và hiện đã lỗi thời. Công cụ cho nhà phát triển vẫn sử dụng được mẫu module.json và chúng tôi rất cố gắng trong việc xoá những tệp này. Trình chặn cuối cùng để xoá những tệp này là phần resources, dùng để tải trong các tệp CSS.

Chúng tôi muốn dành thời gian tìm hiểu nhiều giải pháp tiềm năng mà sau cùng có thể chuyển thành Tập lệnh mô-đun CSS. Mục đích là để loại bỏ các khoản nợ kỹ thuật do hệ thống cũ gây ra, đồng thời giúp quá trình di chuyển sang Tập lệnh mô-đun CSS trở nên dễ dàng hơn.

Mọi tệp CSS trong Công cụ cho nhà phát triển được coi là "cũ" vì chúng được tải bằng tệp module.json đang trong quá trình xóa. Tất cả các tệp CSS phải có trong phần resources trong tệp module.json trong cùng thư mục với tệp CSS.

Ví dụ về tệp module.json còn lại:

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

Sau đó, các tệp CSS này sẽ điền sẵn một mục ánh xạ đối tượng toàn cục có tên là Root.Runtime.cachedResources dưới dạng mục ánh xạ từ đường dẫn đến nội dung của chúng. Để thêm kiểu vào Công cụ cho nhà phát triển, bạn cần gọi registerRequiredCSS kèm theo đường dẫn chính xác đến tệp mà bạn muốn tải.

Ví dụ về cuộc gọi của registerRequiredCSS:

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

Thao tác này sẽ truy xuất nội dung của tệp CSS và chèn nó dưới dạng phần tử <style> vào trang bằng cách sử dụng hàm appendStyle:.

Hàm appendStyle thêm CSS bằng cách sử dụng phần tử kiểu cùng dòng:

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);

Khi ra mắt các thành phần web hiện đại (sử dụng phần tử tuỳ chỉnh), ban đầu chúng tôi quyết định sử dụng CSS thông qua các thẻ <style> cùng dòng trong các tệp thành phần. Quá trình này đặt ra những thách thức riêng:

  • Thiếu tính năng hỗ trợ đánh dấu cú pháp. Các trình bổ trợ cung cấp tính năng làm nổi bật cú pháp cho CSS cùng dòng thường không tốt bằng tính năng đánh dấu cú pháp và tự động hoàn tất các tính năng cho CSS được viết trong tệp .css.
  • Mức hao tổn hiệu suất bản dựng. CSS cùng dòng cũng có nghĩa là cần có hai lượt để tìm lỗi mã nguồn: một cho các tệp CSS và một cho CSS cùng dòng. Đây là chi phí hiệu suất mà chúng tôi có thể loại bỏ nếu tất cả CSS được viết bằng các tệp CSS độc lập.
  • Thách thức trong việc thu nhỏ. Không thể giảm kích thước CSS cùng dòng, vì vậy không có CSS nào được giảm kích thước. Kích thước tệp của bản phát hành cho Công cụ cho nhà phát triển cũng tăng lên do CSS trùng lặp tạo ra từ nhiều phiên bản của cùng một thành phần web.

Mục tiêu của dự án thực tập là tìm ra giải pháp cho cơ sở hạ tầng CSS hoạt động được với cả cơ sở hạ tầng cũ và thành phần web mới đang được sử dụng trong Công cụ cho nhà phát triển.

Nghiên cứu các giải pháp tiềm năng

Vấn đề có thể được chia thành hai phần khác nhau:

  • Tìm hiểu cách hệ thống xây dựng xử lý các tệp CSS.
  • Tìm hiểu cách Công cụ cho nhà phát triển nhập và sử dụng các tệp CSS.

Chúng tôi đã xem xét các giải pháp tiềm năng cho từng phần và trình bày các giải pháp này ở bên dưới.

Nhập các tệp CSS

Mục tiêu của việc nhập và sử dụng CSS trong các tệp TypeScript là bám sát các tiêu chuẩn web nhất có thể, thực thi tính nhất quán trong toàn bộ Công cụ cho nhà phát triển và tránh CSS trùng lặp trong HTML của chúng tôi. Chúng tôi cũng muốn chọn được một giải pháp giúp chúng tôi có thể di chuyển các thay đổi sang các tiêu chuẩn mới dành cho nền tảng web, chẳng hạn như Tập lệnh mô-đun CSS.

Vì những lý do này, câu lệnh @import và thẻ có vẻ không phù hợp với Công cụ cho nhà phát triển. Chúng sẽ không thống nhất với quá trình nhập trong phần còn lại của Công cụ cho nhà phát triển và dẫn đến Đèn flash của nội dung không theo kiểu (FOUC). Quá trình di chuyển sang Tập lệnh mô-đun CSS sẽ khó hơn vì các lệnh nhập sẽ phải được thêm vào và xử lý một cách rõ ràng khác với khi sử dụng thẻ <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>`

Giải pháp tiềm năng bằng cách sử dụng @import hoặc <link>.

Thay vào đó, chúng tôi chọn tìm cách nhập tệp CSS làm đối tượng CSSStyleSheet để có thể thêm tệp này vào Shadow Dom (DevTools đã sử dụng Shadow DOM trong một vài năm nay) bằng cách sử dụng thuộc tính adoptedStyleSheets.

Tùy chọn Bundler

Chúng ta cần có một cách để chuyển đổi các tệp CSS thành đối tượng CSSStyleSheet để có thể dễ dàng thao tác trong tệp TypeScript. Chúng tôi coi cả Rollupwebpack đều là các gói tiềm năng để thực hiện việc chuyển đổi này cho mình. Công cụ cho nhà phát triển đã sử dụng bản tổng hợp trong bản dựng chính thức, nhưng việc thêm một trong hai trình đóng gói vào bản dựng chính thức có thể gặp vấn đề tiềm ẩn về hiệu suất khi làm việc với hệ thống xây dựng hiện tại của chúng tôi. Quá trình tích hợp với hệ thống xây dựng GN của Chromium khiến việc đóng gói trở nên khó khăn hơn. Do đó, các trình đóng gói có xu hướng không tích hợp tốt với hệ thống xây dựng Chromium hiện tại.

Thay vào đó, chúng tôi đã tìm hiểu phương án sử dụng hệ thống xây dựng GN hiện tại để thực hiện việc chuyển đổi này cho chúng tôi.

Cơ sở hạ tầng mới của việc sử dụng CSS trong Công cụ cho nhà phát triển

Giải pháp mới này bao gồm việc sử dụng adoptedStyleSheets để thêm kiểu vào một DOM bóng cụ thể trong khi sử dụng hệ thống xây dựng GN để tạo các đối tượng CSSStyleSheet có thể được document hoặc ShadowRoot áp dụng.

// 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];
  }
}

Việc sử dụng adoptedStyleSheets sẽ mang lại nhiều lợi ích, bao gồm:

  • Web đang trong quá trình trở thành một tiêu chuẩn web hiện đại
  • Ngăn CSS trùng lặp
  • Chỉ áp dụng kiểu cho DOM bóng và điều này giúp tránh mọi vấn đề do tên lớp hoặc bộ chọn mã trùng lặp trong tệp CSS gây ra
  • Dễ dàng di chuyển sang các tiêu chuẩn web trong tương lai như Tập lệnh mô-đun CSS và Xác nhận nhập

Lưu ý duy nhất về giải pháp là các câu lệnh import yêu cầu nhập tệp .css.js. Để cho phép GN tạo tệp CSS trong quá trình xây dựng, chúng tôi đã viết tập lệnh generate_css_js_files.js. Giờ đây, hệ thống xây dựng sẽ xử lý mọi tệp CSS và chuyển đổi tệp đó thành tệp JavaScript. Tệp này sẽ xuất đối tượng CSSStyleSheet theo mặc định. Điều này rất tuyệt vì chúng tôi có thể nhập tệp CSS và sử dụng dễ dàng. Ngoài ra, giờ đây, chúng ta cũng có thể giảm kích thước bản dựng chính thức một cách dễ dàng, giúp tiết kiệm kích thước tệp:

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;

Ví dụ đã tạo iconButton.css.js từ tập lệnh.

Di chuyển mã cũ bằng quy tắc ESLint

Mặc dù bạn có thể dễ dàng di chuyển các thành phần web theo cách thủ công, nhưng quá trình di chuyển mục đích sử dụng cũ của registerRequiredCSS lại phức tạp hơn. Hai hàm chính đã đăng ký kiểu cũ là registerRequiredCSScreateShadowRootWithCoreStyles. Vì các bước di chuyển những lệnh gọi này khá máy móc, nên chúng tôi có thể sử dụng các quy tắc ESLint để áp dụng các bản sửa lỗi và tự động di chuyển mã cũ. Công cụ cho nhà phát triển đã sử dụng một số quy tắc tuỳ chỉnh dành riêng cho cơ sở mã của Công cụ cho nhà phát triển. Điều này rất hữu ích vì hàm ESLint đã phân tích cú pháp mã thành Cây cú pháp trừu tượng(viết tắt: AST) và chúng ta có thể truy vấn các nút lệnh gọi cụ thể là các lệnh gọi để đăng ký CSS.

Vấn đề lớn nhất mà chúng tôi gặp phải khi viết Quy tắc ESLint di chuyển là việc ghi lại các trường hợp hiếm gặp. Chúng tôi muốn đảm bảo có sự cân bằng hợp lý giữa việc biết trường hợp đặc biệt nào đáng ghi nhận và trường hợp nào nên di chuyển theo cách thủ công. Chúng ta cũng muốn đảm bảo có thể thông báo cho người dùng khi tệp .css.js đã nhập không được hệ thống xây dựng tự động tạo, vì điều này giúp ngăn chặn mọi lỗi không tìm thấy tệp trong thời gian chạy.

Một nhược điểm của việc sử dụng quy tắc ESLint cho quá trình di chuyển là chúng tôi không thể thay đổi tệp bản dựng GN bắt buộc trong hệ thống. Người dùng phải thực hiện những thay đổi này theo cách thủ công trong mỗi thư mục. Mặc dù việc này yêu cầu nhiều thao tác hơn, nhưng đó là một cách hay để xác nhận rằng mọi tệp .css.js đang được nhập thực sự do hệ thống xây dựng tạo ra.

Nhìn chung, việc sử dụng các quy tắc ESLint cho quá trình di chuyển này thực sự hữu ích vì chúng tôi có thể nhanh chóng di chuyển mã cũ sang cơ sở hạ tầng mới và có sẵn AST, đồng nghĩa với việc chúng tôi cũng có thể xử lý nhiều trường hợp hiếm gặp trong quy tắc và tự động sửa các lỗi đó một cách đáng tin cậy bằng cách sử dụng API trình sửa lỗi của ESLint.

Tiếp theo sẽ là gì?

Cho đến nay, tất cả các thành phần web trong Công cụ của Chromium cho nhà phát triển đã được di chuyển để sử dụng cơ sở hạ tầng CSS mới thay vì sử dụng kiểu cùng dòng. Hầu hết cách sử dụng cũ của registerRequiredCSS cũng đã được di chuyển sang hệ thống mới. Bạn chỉ cần xoá càng nhiều tệp module.json càng tốt, sau đó di chuyển cơ sở hạ tầng hiện tại này để triển khai Tập lệnh mô-đun CSS trong tương lai!

Tải kênh xem trước xuống

Hãy cân nhắc sử dụng Chrome Canary, Dev hoặc Beta làm trình duyệt phát triển mặc định. Các kênh xem trước này cung cấp cho bạn quyền truy cập vào các tính năng mới nhất của Công cụ cho nhà phát triển, thử nghiệm API nền tảng web tiên tiến và tìm ra sự cố trên trang web của bạn trước khi người dùng của bạn làm điều đó!

Liên hệ với nhóm Công cụ của Chrome cho nhà phát triển

Sử dụng các lựa chọn sau đây để thảo luận về các tính năng mới và thay đổi trong bài đăng hoặc bất cứ vấn đề nào khác liên quan đến Công cụ cho nhà phát triển.

  • Hãy gửi đề xuất hoặc phản hồi cho chúng tôi qua crbug.com.
  • Báo cáo sự cố của Công cụ cho nhà phát triển bằng cách sử dụng phần Tuỳ chọn khác   Thêm   > Trợ giúp > Báo cáo sự cố về Công cụ cho nhà phát triển trong Công cụ cho nhà phát triển.
  • Tweet tại @ChromeDevTools.
  • Hãy để lại bình luận về tính năng mới trong video trên YouTube của Công cụ cho nhà phát triển hoặc video trên YouTube.