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ề tính năng hỗ trợ CSS trong JS trong Công cụ cho nhà phát triển đã ra mắt từ Chrome 85 và nói chung, về ý của chúng tôi khi nói đến CSS trong JS và sự khác biệt của CSS thông thường với CSS thông thường đã được Công cụ cho nhà phát triển hỗ trợ trong một thời gian dài.

CSS trong JS là gì?

Định nghĩa về CSS trong JS khá mơ hồ. Theo nghĩa rộng thì đây là một phương pháp quản lý mã CSS bằng JavaScript. Ví dụ: tình trạng 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 ra một cách nhanh chóng.

Trong bối cảnh của Công cụ cho nhà phát triển, CSS-in-JS có nghĩa là nội dung CSS được đưa vào trang bằ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-in-JS thường không có 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 cách sử dụng CSSOM API, khiến nguồn không đồng bộ với biểu định kiểu CSS thực tế.

Nếu bạn sử dụng bất kỳ thư viện CSS nào trong JS (ví dụ: styled-component, Emotion, JSS), thư viện có thể chèn các kiểu bằng API CSSOM nâng cao tùy thuộc vào chế độ phát triển và trình duyệt.

Hãy xem xét 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ác thư viện CSS trong JS đang thực hiện.

// 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 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ể, đồng thời 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, chúng tôi chỉ hỗ trợ 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 các quy tắc đó. Mục tiêu chính của chúng tôi trong năm ngoái là cho phép chỉnh sửa các quy tắc CSS trong JS bằng cách sử dụng ngăn Kiểu. Đôi khi, chúng tôi cũng gọi kiểu CSS trong JS là "constructed" để cho biết rằng chúng được tạo bằng API Web.

Hãy cùng tìm hiểu chi tiết về hoạt động 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) sẽ đưa ra lệnh CDP có tên là CSS.getMatchedStylesForNode để nhận các quy tắc CSS áp dụng cho phần tử. CDP là viết tắt của Giao thức Chrome cho nhà phát triển và 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ông tin bổ sung 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 đó bằng trình phân tích cú pháp CSS của trình duyệt. Sau đó, Analytics 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 các quy tắc CSS và do đó, trình duyệt không lưu trữ các vị trí này. 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 chịu hậu quả về hiệu suất, nhưng chúng tôi cũng muốn người dùng Công cụ cho nhà phát triển có quyền truy cập vào vị trí nguồn. Phương pháp phân tích cú pháp lại này giải quyết được cả hai trường hợp sử dụng với ít nhược điểm nhất.

Tiếp theo, quá trình triển khai CSS.getMatchedStylesForNode sẽ yêu cầu công cụ tạo kiểu của trình duyệt cung cấp các quy tắc CSS phù hợ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ụ định 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 được 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.

Giờ chúng ta hãy cùng tìm hiểu về khâu biên tập. Bạn có nhớ rằng CSS.getMatchedStylesForNode trả về vị trí nguồn cho mọi quy tắc không? Điều này rất quan trọng cho việc biên tập. Khi bạn thay đổi một 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 mà mảnh cần được 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 các vị trí nguồn cho quy tắc đã cập nhật. Để phản hồi lệnh chỉnh sửa, giao diện người dùng Công cụ cho nhà phát triển sẽ lấy lại các vị trí cập nhật cho đoạn văn bản vừa được cập nhật.

Điều này giải thích tại sao việc chỉnh sửa CSS-in-JS trong Công cụ cho nhà phát triển không hiệu quả: CSS-in-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 hỗ trợ thêm cho CSS-in-JS

Vì vậy, để hỗ trợ việc chỉnh sửa 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ể 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();
}

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

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 được gọi nếu biểu định kiểu không cùng dòng hoặc là 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().

Một trường hợp đặc biệt là các biểu định kiểu liên kết với thẻ <style> đã được thay đổi bằ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. Trong trường hợp 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ử phần tử <style> gốc có 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 dùng API JS để tạo ra thứ tự các quy tắc sau: .Rules0, .rules1, .rules2, .rules3, .rules4. Văn bản nguồn thu được sau thao tác hợp nhất sẽ 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 là rất quan trọng đối với 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à chúng có thể được trang thay đổi 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ì việc chỉnh sửa sẽ không hoạt động. Để làm được điều này, chúng tôi đã ra mắt một cái gọi là đầu 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 thay đổi sẽ được đồng bộ hoá trong lệnh gọi tiếp theo đến CSS.getMATCHStylesForNode.

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ó được tạo hay không. 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ị đúng 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 chúng ta ở đây, chúng ta đã xem qua các trường hợp sử dụng có liên quan đến CSS trong JS mà Công cụ cho nhà phát triển không hỗ trợ và trình bày các giải pháp để hỗ trợ các trường hợp sử dụng đó. Điều thú vị của quá trình triển khai này là chúng ta có thể tận dụng chức năng hiện có bằng cách làm cho các quy tắc CSS của CSSOM có văn bản nguồn thông thường, tránh việc 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 Chromium, trong đó tham chiếu đến tất cả bản vá liên quan.

Tải 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ủa bạn. Các kênh xem trước này cho phép bạ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ử các API nền tảng web tiên tiến và tìm ra các vấn đề trên trang web của bạn trước khi người dùng 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 để thảo luận về các tính năng và thay đổi mới trong bài đăng, hoặc bất cứ nội dung 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ố trong Công cụ cho nhà phát triển bằng cách sử dụng Tuỳ chọn khác   Thêm   > Trợ giúp > Báo cáo vấn đề về 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 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 Mẹo video trên YouTube trong Công cụ cho nhà phát triển.