本文將說明 Chrome 85 之後推出的開發人員工具中,CSS-in-JS 的支援功能,以及 CSS-in-JS 的一般定義,以及與開發人員工具長期支援的一般 CSS 的差異。
什麼是 CSS-in-JS?
CSS-in-JS 的定義相當模糊。從廣義來說,這是一種使用 JavaScript 管理 CSS 程式碼的方法。舉例來說,這可能表示 CSS 內容是使用 JavaScript 定義,而應用程式會即時產生最終 CSS 輸出內容。
在 DevTools 的情況下,CSS-in-JS 是指使用 CSSOM API 將 CSS 內容插入網頁。一般 CSS 會使用 <style>
或 <link>
元素插入,且具有靜態來源 (例如 DOM 節點或網路資源)。相反地,CSS-in-JS 通常沒有靜態來源。特殊情況是,<style>
元素的內容可使用 CSSOM API 更新,導致來源與實際 CSS 樣式表不同步。
如果您使用任何 CSS-in-JS 程式庫 (例如 styled-component、Emotion、JSS),則視開發模式和瀏覽器而定,程式庫可能會在幕後使用 CSSOM API 插入樣式。
讓我們看看如何使用 CSSOM API 注入樣式表,類似於 CSS-in-JS 程式庫的做法。
// 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 支援
在 DevTools 中,處理 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 規則的來源位置,因此不會儲存這些位置。但 DevTools 需要來源位置才能支援 CSS 編輯作業。我們不希望 Chrome 一般使用者因效能受限而受到影響,但我們希望開發人員工具使用者可以存取原始位置。這種重新剖析方法可解決這兩種用途,且缺點極少。
接著,CSS.getMatchedStylesForNode
實作項目會要求瀏覽器的樣式引擎提供與指定元素相符的 CSS 規則。最後,這個方法會將樣式引擎傳回的規則與原始碼建立關聯,並提供 CSS 規則的結構化回應,讓 DevTools 知道規則的哪個部分是選取器或屬性。這可讓開發人員工具獨立編輯選取器和屬性。
接下來,我們來看看如何編輯。您還記得 CSS.getMatchedStylesForNode
會傳回每個規則的原始位置嗎?這對於編輯作業至關重要。變更規則時,開發人員工具會發出另一個 CDP 指令,實際更新網頁。這個指令包含要更新的規則片段原始位置,以及片段需要更新的新文字。
在後端,當開發人員工具處理編輯呼叫時,會更新目標樣式表單。也會更新所維護的樣式表來源副本,並更新更新規則的來源位置。在回應編輯呼叫時,開發人員工具前端會取得更新後的文字片段位置。
這也說明為何在 DevTools 中編輯 CSS-in-JS 無法正常運作:CSS-in-JS 沒有儲存在任何地方的實際來源,且 CSS 規則會在 CSSOM 資料結構中儲存在瀏覽器的記憶體中。
如何新增對 CSS-in-JS 的支援
因此,為了支援編輯 JS 中的 CSS 規則,我們認為最佳解決方案是為建構的樣式表建立來源,以便使用上述現有機制進行編輯。
第一步是建構來源文字。瀏覽器的樣式引擎會將 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 例項,並擷取 DevTools 所需的其他中繼資料:
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);
}
在這個程式碼片段中,我們看到 CSSOMStyleSheetText
在內部呼叫 CollectStyleSheetRules
。如果樣式表單不是內嵌或資源樣式表單,系統會叫用 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 規則與文字版本不同步,編輯功能就無法運作。為此,我們引入了所謂的探針,讓瀏覽器在樣式表變異時通知 DevTools 的後端部分。接著,系統會在下次呼叫 CSS.getMatchedStylesForNode 時,同步處理經過變異的樣式表。
有了這些元素,CSS-in-JS 編輯功能就會運作,但我們希望改善 UI,以便指出是否已建構樣式表。我們已在 CDP 的 CSS.CSSStyleSheetHeader 中新增 isConstructed
屬性,前端可利用這項屬性正確顯示 CSS 規則的來源:
結論
為了重溫這段故事,我們將介紹 DevTools 不支援的 CSS-in-JS 相關用途,並說明如何支援這些用途。這項實作方式最有趣的地方在於,我們可以讓 CSSOM CSS 規則具有一般來源文字,進而運用現有功能,避免在 DevTools 中完全重新設計樣式編輯作業。
如需更多背景資訊,請參閱我們的設計提案或 Chromium 追蹤錯誤,其中列出所有相關修補程式。
下載預覽管道
建議您將 Chrome Canary、開發人員版或Beta 版設為預設開發人員版瀏覽器。這些預覽管道可讓您存取最新的 DevTools 功能,測試最新的網路平台 API,並在使用者發現問題前,協助您找出網站的問題!
與 Chrome 開發人員工具團隊聯絡
請使用下列選項討論新功能、更新或任何與開發人員工具相關的內容。
- 請前往 crbug.com 提交意見回饋和功能要求。
- 在開發人員工具中,依序按一下「more_vert」 更多選項 >「Help」 >「Report a DevTools issue」,即可回報開發人員工具的問題。
- 在 Twitter 上傳送訊息給 @ChromeDevTools。
- 在 YouTube 影片「What's new in DevTools」或「開發人員工具提示」YouTube 影片中留言。