開發人員工具中的 CSS-in-JS 支援

Alex Rudenko
Alex Rudenko

本文將介紹自 Chrome 85 版起,開發人員對開發人員工具中的 CSS-in-JS 支援功能,以及整體來說,CSS-in-JS 的意思,以及開發人員工具和開發人員工具長期支援的一般 CSS 有何不同。

什麼是 CSS-in-JS?

CSS-in-JS 的定義並不明確。大致上來說,這是使用 JavaScript 管理 CSS 程式碼的方法。舉例來說,這可能表示 CSS 內容是以 JavaScript 定義,而且應用程式會即時產生最終的 CSS 輸出內容。

在開發人員工具中,CSS-in-JS 代表使用 CSSOM API 將 CSS 內容插入網頁。一般的 CSS 是使用 <style><link> 元素插入,且具有靜態來源 (例如 DOM 節點或網路資源)。相對地,CSS-in-JS 通常沒有靜態來源。這裡的特殊情況是,<style> 元素的內容可以使用 CSSOM API 更新,導致來源和實際的 CSS 樣式表不一致。

如果您使用任何 CSS-in-JS 程式庫 (例如 styled-componentEmotionJSS),程式庫可能會根據開發模式和瀏覽器,在背景使用 CSSOM API 插入樣式。

以下舉例說明如何使用與 CSS-in-JS 程式庫類似的 CSSOM API 插入樣式表。

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

您也可以建立新的樣式表

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

開發人員工具中的 CSS 支援

開發人員工具中最常用於處理 CSS 的功能是「Styles」窗格。在「樣式」窗格中,您可以查看特定元素套用的規則,也可以編輯規則並即時在網頁上查看變更。

去年,對於使用 CSSOM API 修改的 CSS 規則所支援的支援程度很有限:您只能查看已套用的規則,但無法編輯。我們去年的主要目標是允許使用「樣式」窗格編輯 CSS-in-JS 規則。有時我們也會將 CSS-in-JS 樣式稱為「建構」,表示這些樣式是使用 Web API 建構而成。

接著就來進一步瞭解開發人員工具中樣式編輯作業的運作方式。

開發人員工具中的樣式編輯機制

開發人員工具中的樣式編輯機制

在開發人員工具中選取元素後,「Styles」窗格就會顯示。「Styles」窗格會發出名為 CSS.getMatchedStylesForNode 的 CDP 指令,取得套用到元素的 CSS 規則。CDP 是指 Chrome 開發人員工具通訊協定,這個 API 可讓開發人員工具前端取得受檢查網頁的額外資訊。

叫用時,CSS.getMatchedStylesForNode 會識別文件中的所有樣式表,並使用瀏覽器的 CSS 剖析器進行剖析。然後建立一個索引,將每個 CSS 規則與樣式表來源中的位置建立關聯。

您可能會好奇,為什麼需要再次剖析 CSS?這個問題的原因是,瀏覽器本身與 CSS 規則的來源位置無關,因此不會儲存這些規則。不過,開發人員工具需要來源位置才能支援 CSS 編輯功能。我們不希望一般 Chrome 使用者支付效能懲罰,但我們希望開發人員工具的使用者能夠存取來源位置。這種重新剖析方法能以最少的缺點,滿足這兩種使用情境。

接下來,CSS.getMatchedStylesForNode 實作會要求瀏覽器的樣式引擎提供符合指定元素的 CSS 規則。最後,這個方法會將樣式引擎傳回的規則與原始碼建立關聯,並提供 CSS 規則的結構化回應,讓開發人員工具知道規則的哪個部分是選取器或屬性。可讓開發人員工具獨立編輯選取器和屬性。

接著來看看編輯請注意,CSS.getMatchedStylesForNode 會傳回每項規則的來源位置嗎?編輯時十分重要,變更規則時,開發人員工具會發出另一個 CDP 指令,實際更新網頁。這個指令包含正在更新的規則片段原始位置,以及需要更新片段的新文字。

在後端處理編輯呼叫時,開發人員工具會更新目標樣式表。它也會更新自己維護的樣式表來源複本,並更新更新規則的來源位置。為回應編輯呼叫,開發人員工具前端會恢復剛更新的文字片段的更新位置。

這說明瞭為何在開發人員工具中編輯 CSS-in-JS 無法立即執行:CSS-in-JS 沒有任何實際的來源儲存位置,以及「CSS 規則存在於 CSSOM 資料結構中的瀏覽器記憶體中」

我們如何新增 CSS-in-JS 支援

因此,為支援編輯 CSS-in-JS 規則,我們決定最好的解決方式是建立建構樣式表的來源,讓使用者可以使用上述現有機制加以編輯。

首先建構來源文字。瀏覽器的樣式引擎會將 CSS 規則儲存在 CSSStyleSheet 類別中。如前所述,就是透過 JavaScript 建立例項的類別。建構原始碼的程式碼如下所示:

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

疊代處理在 CSSStyleSheet 執行個體中找到的規則,然後從中建構單一字串。建立 InspectorStyleSheet 類別的例項時,系統會叫用這個方法。InspectorStyleSheet 類別會納入 CSSStyleSheet 執行個體,並擷取開發人員工具所需的其他中繼資料:

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

在這段程式碼中,我們看到在內部呼叫 CollectStyleSheetRulesCSSOMStyleSheetText。如果樣式表不是內嵌或在資源樣式表中,則會叫用 CSSOMStyleSheetText。基本上,這兩個程式碼片段已經允許對使用 new CSSStyleSheet() 建構函式建立的樣式表進行基本編輯。

特殊情況是指與 <style> 標記相關聯,且使用 CSSOM API 變更的樣式表。在此情況下,樣式表會包含來源文字以及不在來源中的其他規則。為處理這個情況,我們引進了將額外規則合併到來源文字的方法。這裡的順序很重要,因為可以在原始來源文字的中間插入 CSS 規則。舉例來說,假設原始 <style> 元素包含以下文字:

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

接著,網頁使用 JS API 插入一些新規則,這些規則的順序如下: .rule0、.rule1、.rule2、.rule3、.rule4合併作業之後產生的來源文字應如下所示:

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

由於規則的來源文字位置必須精確,因此系統會保留原始的註解和縮排。

CSS-in-JS 樣式表的另一項特別之處,在於網頁可隨時變更。如果實際的 CSSOM 規則與文字版本不同步,編輯就會失敗。為此,我們導入了一項稱為 probe,可讓瀏覽器在樣式表發生變化時,讓瀏覽器通知開發人員工具的後端。變更後的樣式表會在下次呼叫 CSS.getMatchedStylesForNode 時同步處理。

完成這些部分後,CSS-in-JS 編輯功能已可正常運作,但我們還是想改善使用者介面,指出是否已建立樣式表。我們已在 CDP 的 CSS.CSSStyleSheetHeader 中新增名為 isConstructed 的新屬性,以便前端正確顯示 CSS 規則的來源:

可建構的樣式表

結論

在此回顧一下,我們介紹了開發人員工具不支援的 CSS-in-JS 相關用途,並逐步介紹支援這些用途的解決方案。這項實作的優點在於,我們可以讓 CSSOM CSS 規則使用一般的原文文字,藉此運用現有功能,而無須在開發人員工具中全面重新設定樣式編輯功能。

如需更多背景資訊,請參閱我們的設計提案或 Chromium 追蹤錯誤,其中包含所有相關修補程式。

下載預覽頻道

建議您使用 Chrome CanaryDevBeta 版做為預設的開發瀏覽器。透過這些預覽版本,您可以存取開發人員工具中的最新功能、測試最先進的網路平台 API,以及找出網站的問題,以免使用者發現問題。

與 Chrome 開發人員工具團隊聯絡

請使用下列選項,討論貼文中的新功能和異動,或與開發人員工具相關的其他事項。

  • 歡迎透過 crbug.com 提出建議或意見。
  • 使用「更多選項」更多 > 回報開發人員工具問題說明 >在開發人員工具中回報開發人員工具問題
  • 前往 @ChromeDevTools 張貼 Tweet。
  • 歡迎在「開發人員工具」推出「最新消息」YouTube 影片或「開發人員工具秘訣」YouTube 影片留言。