Hỗ trợ CSS trong JS trong Công cụ cho nhà phát triển

Alex Rudenko
Alex Rudenko

Bài viết này nói về việc hỗ trợ CSS trong JS trong Công cụ cho nhà phát triển có sẵn kể từ Chrome 85 và nói chung ý nghĩa của việc CSS trong JS cũng như sự khác biệt giữa CSS trong công cụ cho nhà phát triển trong một thời gian dài.

CSS trong JS là gì?

Định nghĩa về CSS trong JS khá mơ hồ. Nói chung, đây là phương pháp quản lý mã CSS bằng JavaScript. Ví dụ: điều này có thể có nghĩa là nội dung CSS được xác định bằng cách sử dụng JavaScript và đầu ra CSS cuối cùng được ứng dụng tạo nhanh chóng.

Trong bối cảnh của Công cụ cho nhà phát triển, CSS trong JS có nghĩa là nội dung CSS được đưa vào trang bằng cách sử dụng API CSSOM. CSS thông thường được chèn bằng các phần tử <style> hoặc <link> và có nguồn tĩnh (ví dụ: nút DOM hoặc tài nguyên mạng). Ngược lại, CSS trong JS thường không có một nguồn tĩnh. Một trường hợp đặc biệt ở đây là nội dung của phần tử <style> có thể được cập nhật bằng CSSOM API, khiến nguồn không đồng bộ với biểu định kiểu CSS thực tế.

Về bản chất, nếu bạn sử dụng thư viện CSS trong JS bất kỳ (ví dụ: được định kiểu thành phần, Cảm xúc, JSS), thì thư viện có thể chèn các kiểu bằng API CSSOM về nâng cao tuỳ thuộc vào chế độ phát triển và trình duyệt.

Hãy xem một số ví dụ về cách bạn có thể chèn biểu định kiểu bằng API CSSOM, tương tự như cách thư viện CSS trong JS đang làm.

// 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; }');

Bạn cũng có thể tạo một biểu định kiểu hoàn toàn mới:

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

Hỗ trợ CSS trong Công cụ cho nhà phát triển

Trong Công cụ cho nhà phát triển, tính năng thường dùng nhất khi xử lý CSS là ngăn Kiểu. Trong ngăn Kiểu, bạn có thể xem những quy tắc áp dụng cho một phần tử cụ thể, cũng như chỉnh sửa quy tắc và xem các thay đổi trên trang theo thời gian thực.

Trước năm ngoái, hỗ trợ cho các quy tắc CSS được sửa đổi bằng API CSSOM khá hạn chế: bạn chỉ có thể xem các quy tắc được áp dụng nhưng không thể chỉnh sửa chúng. Mục tiêu chính của chúng tôi năm ngoái là cho phép chỉnh sửa các quy tắc CSS trong JS bằng ngăn Kiểu. Đôi khi, chúng tôi cũng gọi các kiểu CSS trong JS là "constructed" để cho biết rằng các kiểu này được tạo bằng Web API.

Hãy cùng tìm hiểu chi tiết về cách chỉnh sửa Kiểu trong Công cụ cho nhà phát triển.

Cơ chế chỉnh sửa kiểu trong Công cụ cho nhà phát triển

Cơ chế chỉnh sửa kiểu trong Công cụ cho nhà phát triển

Khi bạn chọn một phần tử trong Công cụ cho nhà phát triển, ngăn Kiểu sẽ hiển thị. Ngăn Styles (Kiểu) tạo lệnh CDP có tên là CSS.getMatchedStylesForNode để lấy các quy tắc CSS áp dụng cho phần tử. CDP là viết tắt của Giao thức Công cụ của Chrome cho nhà phát triển và đây là một API cho phép giao diện người dùng Công cụ cho nhà phát triển nhận thêm thông tin về trang được kiểm tra.

Khi được gọi, CSS.getMatchedStylesForNode sẽ xác định tất cả biểu định kiểu trong tài liệu và phân tích cú pháp các biểu định kiểu đó bằng trình phân tích cú pháp CSS của trình duyệt. Sau đó, thao tác này sẽ tạo một chỉ mục liên kết mọi quy tắc CSS với một vị trí trong nguồn biểu định kiểu.

Bạn có thể hỏi tại sao cần phải phân tích cú pháp CSS một lần nữa? Vấn đề ở đây là vì lý do hiệu suất, bản thân trình duyệt không quan tâm đến vị trí nguồn của quy tắc CSS và do đó, không lưu trữ các vị trí đó. Tuy nhiên, Công cụ cho nhà phát triển cần có vị trí nguồn để hỗ trợ chỉnh sửa CSS. Chúng tôi không muốn người dùng Chrome thông thường phải trả phí về hiệu suất, nhưng chúng tôi muốn người dùng Công cụ cho nhà phát triển có quyền truy cập vào các vị trí nguồn. Phương pháp phân tích cú pháp lại này giải quyết cả hai trường hợp sử dụng với rất ít nhược điểm.

Tiếp theo, việc triển khai CSS.getMatchedStylesForNode yêu cầu công cụ định kiểu của trình duyệt cung cấp các quy tắc CSS khớp với phần tử nhất định. Cuối cùng, phương thức này liên kết các quy tắc do công cụ tạo kiểu trả về với mã nguồn và cung cấp phản hồi có cấu trúc về các quy tắc CSS để Công cụ cho nhà phát triển biết phần nào của quy tắc là bộ chọn hoặc thuộc tính. Công cụ này cho phép Công cụ cho nhà phát triển chỉnh sửa bộ chọn và các thuộc tính một cách độc lập.

Bây giờ, hãy xem cách chỉnh sửa. Bạn có nhớ rằng CSS.getMatchedStylesForNode trả về vị trí nguồn cho mọi quy tắc không? Điều đó rất quan trọng đối với việc chỉnh sửa. Khi bạn thay đổi quy tắc, Công cụ cho nhà phát triển sẽ đưa ra một lệnh CDP khác thực sự cập nhật trang. Lệnh này bao gồm vị trí ban đầu của mảnh quy tắc đang được cập nhật và văn bản mới cần cập nhật.

Trên phần phụ trợ, khi xử lý lệnh gọi chỉnh sửa, Công cụ cho nhà phát triển sẽ cập nhật biểu định kiểu mục tiêu. Thao tác này cũng cập nhật bản sao của nguồn biểu định kiểu mà nó duy trì và cập nhật vị trí nguồn cho quy tắc đã cập nhật. Để phản hồi lệnh gọi chỉnh sửa, giao diện người dùng Công cụ cho nhà phát triển sẽ nhận lại các vị trí cập nhật cho đoạn văn bản vừa cập nhật.

Điều này giải thích tại sao việc chỉnh sửa CSS trong JS trong Công cụ cho nhà phát triển không hoạt động hiệu quả: CSS-trong-JS không có nguồn thực tế được lưu trữ ở bất cứ đâucác quy tắc CSS nằm trong bộ nhớ của trình duyệt trong cấu trúc dữ liệu CSSOM.

Cách chúng tôi thêm tính năng hỗ trợ cho CSS trong JS

Vì vậy, để hỗ trợ việc chỉnh sửa các quy tắc CSS trong JS, chúng tôi quyết định giải pháp tốt nhất là tạo một nguồn cho các biểu định kiểu đã tạo có thể chỉnh sửa được bằng cơ chế hiện có được mô tả ở trên.

Bước đầu tiên là tạo văn bản nguồn. Công cụ định kiểu của trình duyệt lưu trữ các quy tắc CSS trong lớp CSSStyleSheet. Lớp đó là lớp có các thực thể mà bạn có thể tạo từ JavaScript như đã thảo luận trước đó. Mã để tạo văn bản nguồn như sau:

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

Thao tác này lặp lại các quy tắc có trong một thực thể CSSStyleSheet rồi tạo một chuỗi duy nhất từ thực thể đó. Phương thức này được gọi khi tạo một thực thể của lớp InspectorStyleSheet. Lớp InspectorStyleSheet bao bọc một thực thể CSSStyleSheet và trích xuất siêu dữ liệu bổ sung mà Công cụ cho nhà phát triển yêu cầu:

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

Trong đoạn mã này, chúng ta thấy CSSOMStyleSheetText gọi CollectStyleSheetRules nội bộ. CSSOMStyleSheetText sẽ được gọi nếu biểu định kiểu không cùng dòng hoặc biểu định kiểu tài nguyên. Về cơ bản, 2 đoạn mã này đã cho phép chỉnh sửa cơ bản các biểu định kiểu được tạo bằng hàm khởi tạo new CSSStyleSheet().

Trường hợp đặc biệt là các biểu định kiểu liên kết với thẻ <style> đã bị thay đổi bằng cách sử dụng CSSOM API. Trong trường hợp này, biểu định kiểu chứa văn bản nguồn và các quy tắc bổ sung không có trong nguồn. Để xử lý trường hợp này, chúng tôi giới thiệu một phương thức để hợp nhất các quy tắc bổ sung đó vào văn bản nguồn. Ở đây, thứ tự rất quan trọng vì các quy tắc CSS có thể được chèn vào giữa văn bản nguồn ban đầu. Ví dụ: giả sử rằng phần tử <style> ban đầu chứa văn bản sau:

/* comment */
.rule1 {}
.rule3 {}

Sau đó, trang này đã chèn một số quy tắc mới bằng cách sử dụng API JS tạo thứ tự các quy tắc sau đây: .quy tắc 0, quy tắc 1, .quy tắc 2, .quy tắc 3, .quy tắc4. Văn bản nguồn thu được sau thao tác hợp nhất phải như sau:

.rule0 {}
/* comment */
.rule1 {}
.rule2 {}
.rule3 {}
.rule4 {}

Việc giữ lại các nhận xét ban đầu và thụt đầu dòng rất quan trọng trong quá trình chỉnh sửa vì vị trí văn bản nguồn của các quy tắc phải chính xác.

Một khía cạnh đặc biệt khác đối với biểu định kiểu CSS trong JS là trang có thể thay đổi biểu định kiểu bất cứ lúc nào. Nếu các quy tắc CSSOM thực tế không đồng bộ với phiên bản văn bản thì tính năng chỉnh sửa sẽ không hoạt động. Để làm được điều này, chúng tôi đã giới thiệu một cái gọi là thăm dò, cho phép trình duyệt thông báo cho phần phụ trợ của Công cụ cho nhà phát triển khi biểu định kiểu đang được thay đổi. Sau đó, các biểu định kiểu bị thay đổi sẽ được đồng bộ hoá trong lệnh gọi tiếp theo đến CSS.get matchingStylesForNode.

Với tất cả các phần này, tính năng chỉnh sửa CSS trong JS đã hoạt động, nhưng chúng tôi muốn cải thiện giao diện người dùng để cho biết liệu biểu định kiểu đã được tạo hay chưa. Chúng tôi đã thêm một thuộc tính mới có tên là isConstructed vào CSS.CSSStyleSheetHeader của CDP mà giao diện người dùng sử dụng để hiển thị chính xác nguồn của quy tắc CSS:

Biểu định kiểu có thể tạo

Kết luận

Để tóm tắt câu chuyện của mình ở đây, chúng ta đã tìm hiểu những trường hợp sử dụng liên quan đến CSS trong JS mà Công cụ cho nhà phát triển không hỗ trợ, đồng thời hướng dẫn về giải pháp để hỗ trợ những trường hợp sử dụng đó. Điều thú vị của việc triển khai này là chúng tôi đã có thể tận dụng chức năng hiện có bằng cách tạo cho các quy tắc CSSOM CSS có văn bản nguồn thông thường, giúp tránh phải thiết kế lại hoàn toàn việc chỉnh sửa kiểu trong Công cụ cho nhà phát triển.

Để biết thêm thông tin cơ bản, hãy xem đề xuất thiết kế của chúng tôi hoặc lỗi theo dõi của Chromium tham chiếu đến tất cả các bản vá liên quan.

Tải các 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. Những 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, kiểm thử API nền tảng web tiên tiến và tìm ra các sự cố trên trang web của bạn trước khi người dùng làm việc đó!

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

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

  • 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ông cụ cho nhà phát triển bằng cách sử dụng mục Tuỳ chọn khác   Thêm   > Trợ giúp > Báo cáo sự cố Công cụ cho nhà phát triển trong Công cụ cho nhà phát triển.
  • Tweet tại @ChromeDevTools.
  • Để lại nhận xét về Video trên YouTube của chúng tôi 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 mẹo Công cụ cho nhà phát triển.