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 là một phần trong một 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ủa DevTools và cách xây dựng cấu trúc đó. Chúng tôi sẽ giải thích cách CSS hoạt động trong DevTools trước đây và cách chúng tôi hiện đại hoá CSS trong DevTools để chuẩn bị (cuối cùng) chuyển sang giải pháp chuẩn web để tải CSS trong các tệp JavaScript.
Trạng thái trước đó của CSS trong DevTools
DevTools đã triển khai CSS theo hai cách khác nhau: một cách cho các tệp CSS được sử dụng trong phần cũ của DevTools, một cách cho các thành phần web hiện đại đang được sử dụng trong DevTools.
Việc triển khai CSS trong DevTools đượ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 gặp khó khăn khi sử dụng mẫu module.json
và đã có nỗ lực rất lớn để xoá những tệp này. Trình chặn cuối cùng để xoá các tệp này là phần resources
, dùng để tải các tệp CSS.
Chúng tôi muốn dành thời gian khám phá nhiều giải pháp tiềm năng mà sau này 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 DevTools đều được coi là "cũ" vì chúng được tải bằng tệp module.json
đang trong quá trình bị xoá. Tất cả tệp CSS phải được liệt kê trong resources
trong tệp module.json
ở 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 vào một bản đồ đối tượng toàn cục có tên là Root.Runtime.cachedResources
dưới dạng bản đồ ánh xạ từ một đường dẫn đến nội dung của các tệp đó. Để thêm kiểu vào Công cụ cho nhà phát triển, bạn cần gọi registerRequiredCSS
có đường dẫn chính xác đến tệp bạn muốn tải.
Ví dụ về lệnh gọi 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ội dung đó dưới dạng phần tử <style>
vào trang bằng hàm appendStyle
:.
Hàm appendStyle
thêm CSS bằng phần tử kiểu nội tuyến:
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 giới thiệu các thành phần web hiện đại (sử dụng các phần tử tuỳ chỉnh), chúng tôi ban đầu quyết định sử dụng CSS thông qua các thẻ <style>
nội tuyến trong chính các tệp thành phần. Chính điều này cũng đặt ra những thách thức riêng:
- Thiếu tính năng hỗ trợ làm nổi bật 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 nội tuyến thường không tốt bằng tính năng làm nổi bật cú pháp và tự động hoàn thành cho CSS được viết trong tệp
.css
. - Tăng chi phí hiệu suất của bản dựng. CSS nội tuyến cũng có nghĩa là cần phải có hai lượt truyền để tìm lỗi mã nguồn: một cho 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 trong các tệp CSS độc lập.
- Thách thức trong việc rút gọn. Không thể dễ dàng rút gọn CSS nội tuyến, vì vậy, không có CSS nào được rút gọn. Kích thước tệp của bản phát hành DevTools cũng tăng lên do CSS trùng lặp do nhiều thực thể của cùng một thành phần web gây ra.
Mục tiêu của dự án thực tập của tôi là tìm giải pháp cho cơ sở hạ tầng CSS hoạt động với cả cơ sở hạ tầng cũ và các thành phần web mới đang được sử dụng trong DevTools.
Nghiên cứu các giải pháp tiềm năng
Vấn đề này có thể được chia thành hai phần:
- 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ác tệp CSS được nhập và sử dụng bởi Công cụ của Chrome cho nhà phát triển.
Chúng tôi đã xem xét nhiều giải pháp tiềm năng cho từng phần và các giải pháp này được nêu ở bên dưới.
Nhập 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à tuân thủ các tiêu chuẩn web càng gần càng tốt, thực thi tính nhất quán trong DevTools và tránh CSS trùng lặp trong HTML. Chúng tôi cũng muốn có thể chọn một giải pháp cho phép di chuyển các thay đổi của mình sang các tiêu chuẩn nền tảng web mới, chẳng hạn như Tập lệnh mô-đun CSS.
Vì những lý do này, các câu lệnh @import và thẻ có vẻ không phù hợp với DevTools. Các tệp này sẽ không đồng nhất với các tệp nhập trong phần còn lại của DevTools và dẫn đến Flash Of Unstyled Content (FOUC) (Nội dung không được định kiểu xuất hiện trong giây lát). Việc di chuyển sang tập lệnh mô-đun CSS sẽ khó khăn hơn vì bạn phải thêm và xử lý các tệp nhập một cách rõ ràng hơn so với 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>`
Các giải pháp tiềm năng sử dụng @import
hoặc <link>
.
Thay vào đó, chúng ta chọn tìm cách nhập tệp CSS dưới dạng đối tượng CSSStyleSheet
để có thể thêm tệp đó vào Shadow Dom (DevTools sử dụng Shadow DOM trong vài năm nay) bằng cách sử dụng thuộc tính adoptedStyleSheets
.
Tuỳ chọn trình tạo gói
Chúng ta cần một cách để chuyển đổi tệp CSS thành đối tượng CSSStyleSheet
để có thể dễ dàng thao tác với tệp đó trong tệp TypeScript. Chúng tôi đã xem xét cả Rollup và webpack làm trình đóng gói tiềm năng để thực hiện việc chuyển đổi này. DevTools đã sử dụng Rollup trong bản dựng chính thức, nhưng việc thêm trình tạo gói vào bản dựng chính thức có thể gây ra các vấ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. Việc 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 đó, 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 đã khám phá lựa chọ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.
Cơ sở hạ tầng mới để sử dụng CSS trong DevTools
Giải pháp mới này sử dụng adoptedStyleSheets
để thêm kiểu vào một Shadow DOM cụ thể trong khi sử dụng hệ thống xây dựng GN để tạo các đối tượng CSSStyleSheet mà document
hoặc ShadowRoot
có thể sử 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
mang lại nhiều lợi ích, bao gồm:
- Đây đang là tiêu chuẩn web hiện đại
- Ngăn CSS trùng lặp
- Chỉ áp dụng kiểu cho Shadow DOM và điều này giúp tránh mọi vấn đề do tên lớp trùng lặp hoặc bộ chọn mã nhận dạng trong tệp CSS
- 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à Nhập Xác nhận
Lưu ý duy nhất về giải pháp này là câu lệnh import
yêu cầu nhập tệp .css.js
. Để cho phép GN tạo một tệp CSS trong quá trình tạo bản dựng, chúng ta đã viết tập lệnh generate_css_js_files.js
. Hệ thống xây dựng hiện xử lý mọi tệp CSS và chuyển đổi tệp đó thành tệp JavaScript mà theo mặc định sẽ xuất đối tượng CSSStyleSheet
. Điều này thật tuyệt vì chúng ta có thể dễ dàng nhập và sử dụng tệp CSS. Hơn nữa, giờ đây, chúng ta cũng có thể dễ dàng rút gọn bản phát hành chính thức, 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ù các thành phần web có thể dễ dàng di chuyển theo cách thủ công, nhưng quá trình di chuyển các cách sử dụng cũ của registerRequiredCSS
lại phức tạp hơn. Hai hàm chính đã đăng ký các kiểu cũ là registerRequiredCSS
và createShadowRootWithCoreStyles
. Chúng tôi quyết định rằng vì các bước di chuyển các lệnh gọi này khá cơ họ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ũ. DevTools đã sử dụng một số quy tắc tuỳ chỉnh dành riêng cho cơ sở mã DevTools. Cách này hữu ích vì ESLint đã phân tích cú pháp mã thành Abstract Syntax Tree(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à 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à ghi lại các trường hợp hiếm gặp. Chúng tôi muốn đảm bảo cân bằng hợp lý giữa việc biết được những trường hợp hiếm gặp nào đáng ghi nhận và những trường hợp nào nên được di chuyển theo cách thủ công. Chúng tôi cũng muốn đảm bảo rằng có thể thông báo cho người dùng khi hệ thống xây dựng không tự động tạo tệp .css.js
đã nhập, vì điều này sẽ ngăn mọi lỗi không tìm thấy tệp trong thời gian chạy.
Một bất lợi của việc sử dụng các 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 các thay đổi này theo cách thủ công trong mỗi thư mục. Mặc dù việc này đòi hỏi nhiều công sức hơn, nhưng đây 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 rất 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à việc có sẵn AST có nghĩa là 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 khắc phục các trường hợp đó một cách đáng tin cậy bằng cách sử dụng API trình khắc phục của ESLint.
Ðiều gì kế tiếp?
Cho đến nay, tất cả các thành phần web trong Chromium DevTools đã đượ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ác cách sử dụng cũ của registerRequiredCSS
cũng đã được di chuyển để sử dụng hệ thống mới. Bạn chỉ cần xoá nhiều tệp module.json
nhất có thể rồi 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 các kênh xem trước xuống
Hãy cân nhắc việc sử dụng Chrome Canary, Nhà phát triển 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 cho phép bạn sử dụng các tính năng mới nhất của DevTools, kiểm thử các API nền tảng web tiên tiến và giúp bạn tìm thấy vấn đề trên trang web của mình trước khi người dùng phát hiện ra!
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 để thảo luận về các tính năng mới, bản cập nhật hoặc bất cứ điều gì khác liên quan đến Công cụ cho nhà phát triển.
- Gửi ý kiến phản hồi và yêu cầu về tính năng cho chúng tôi tại crbug.com.
- Báo cáo sự cố trong Công cụ cho nhà phát triển bằng cách sử dụng Tuỳ chọn khác > Trợ giúp > Báo cáo sự cố trong Công cụ cho nhà phát triển trong Công cụ cho nhà phát triển.
- Gửi tweet đến @ChromeDevTools.
- Để lại bình luận trên video YouTube về tính năng mới trong DevTools hoặc video YouTube về mẹo sử dụng DevTools.