Houdini:也許是你從未聽說過的最激動人心的 CSS 開發

已發表: 2022-03-10
快速總結 ↬您是否曾經想使用特定的 CSS 功能,但因為並非所有瀏覽器都完全支持而沒有使用? 或者,更糟糕的是,所有瀏覽器都支持它,但支持有缺陷、不一致甚至完全不兼容? 如果這發生在你身上——我敢打賭它已經發生了——那麼你應該關心 Houdini。 Houdini 是一個新的 W3C 工作組,其最終目標是讓這個問題永遠消失。 它計劃通過引入一組新的 API 來做到這一點,這些 API 將首次賦予開發人員擴展 CSS 本身的能力,以及掛鉤到瀏覽器渲染引擎的樣式和佈局過程的工具。

您是否曾經想使用特定的 CSS 功能,但因為並非所有瀏覽器都完全支持而沒有使用? 或者,更糟糕的是,所有瀏覽器都支持它,但支持有缺陷、不一致甚至完全不兼容? 如果這發生在你身上——我敢打賭它已經發生了——那麼你應該關心 Houdini。

Houdini 是一個新的 W3C 工作組,其最終目標是讓這個問題永遠消失。 它計劃通過引入一組新的 API 來做到這一點,這些 API 將首次賦予開發人員擴展 CSS 本身的能力,以及掛鉤到瀏覽器渲染引擎的樣式和佈局過程的工具。

關於 SmashingMag 的進一步閱讀

  • 為什麼應該停止在本地安裝 WebDev 環境
  • CSS 的未來:實驗性 CSS 屬性
  • 53 種你離不開的 CSS 技術

但這具體是什麼意思? 這甚至是個好主意嗎? 它將如何幫助我們的開發人員現在和將來構建網站?

跳躍後更多! 繼續往下看↓

在本文中,我將嘗試回答這些問題。 但在我這樣做之前,重要的是要弄清楚今天的問題是什麼以及為什麼需要改變。 然後,我將更具體地討論 Houdini 將如何解決這些問題,並列出目前正在開發的一些更令人興奮的功能。 最後,我將提供一些我們作為 Web 開發人員今天可以做的具體事情,以幫助 Houdini 成為現實。

胡迪尼試圖解決什麼問題?

每當我寫一篇文章或構建一個展示一些全新 CSS 功能的演示時,不可避免地會有人在評論或 Twitter 上說,“這太棒了! 太糟糕了,我們將無法再使用它 10 年。”

像這樣的評論既煩人又不具建設性,我理解這種情緒。 從歷史上看,功能提案需要數年時間才能獲得廣泛採用。 原因在於,在整個網絡歷史中,將新功能添加到 CSS 中的唯一方法是通過標準流程。

標準流程
標準過程中的步驟。 (查看大圖)

雖然我絕對沒有反對標準流程,但不可否認這可能需要很長時間!

例如,flexbox 於 2009 年首次提出,開發者至今仍抱怨由於缺乏瀏覽器支持而無法使用。 當然,這個問題正在慢慢消失,因為現在幾乎所有現代瀏覽器都會自動更新。 但即使使用現代瀏覽器,提案與功能的普遍可用性之間總會存在滯後。

有趣的是,並非所有網絡領域都是如此。 考慮一下最近在 JavaScript 中的工作情況:

填充工藝
polyfill 過程中的步驟。 (查看大圖)

在這種情況下,從產生想法到在生產中使用它之間的時間有時可能只是幾天的時間。 我的意思是,我已經在生產環境中使用了async / await功能,而且該功能甚至沒有在單個瀏覽器中實現!

您還可以看到這兩個社區的總體情緒存在巨大差異。 在 JavaScript 社區中,您會讀到人們抱怨事情進展得太快的文章。 另一方面,在 CSS 中,你會聽到人們抱怨學習任何新東西是徒勞的,因為他們需要很長時間才能真正使用它。

那麼,為什麼我們不寫更多的 CSS Polyfills 呢?

乍一看,寫更多的 CSS polyfills 似乎是答案。 有了好的 polyfill,CSS 可以像 JavaScript 一樣快,對吧?

可悲的是,事情並沒有那麼簡單。 填充 CSS 非常困難,並且在大多數情況下,不可能以不完全破壞性能的方式進行。

JavaScript 是一種動態語言,這意味著您可以使用 JavaScript 來填充 JavaScript。 而且因為它是如此動態,所以它的可擴展性極強。 另一方面,CSS 很少用於填充 CSS。 在某些情況下,您可以在構建步驟中將 CSS 轉換為 CSS(PostCSS 會這樣做); 但是如果你想填充任何依賴於 DOM 的結構或元素的佈局或位置的東西,那麼你必須運行你的 polyfill 的邏輯客戶端。

不幸的是,瀏覽器並不容易做到這一點。

下面的圖表給出了您的瀏覽器如何從接收 HTML 文檔到在屏幕上顯示像素的基本概述。 藍色的步驟顯示了 JavaScript 可以控制結果的地方:

渲染過程
JavaScript 訪問瀏覽器的渲染管道。 (查看大圖)

畫面相當慘淡。 作為開發人員,您無法控制瀏覽器如何解析 HTML 和 CSS 並將其轉換為 DOM 和 CSS 對像模型 (CSSOM)。 您無法控制級聯。 您無法控制瀏覽器如何選擇在 DOM 中佈局元素或如何在屏幕上直觀地繪製這些元素。 而且您無法控制合成器的功能。

您可以完全訪問的進程的唯一部分是 DOM。 CSSOM 有點開放。 然而,引用 Houdini 網站的話,它“未指定,跨瀏覽器不一致,並且缺少關鍵功能。”

例如,今天瀏覽器中的 CSSOM 不會向您顯示跨域樣式表的規則,它會簡單地丟棄任何它不理解的 CSS 規則或聲明,這意味著如果您想在瀏覽器中填充一個特性不支持它,你不能使用CSSOM。 相反,您必須遍歷 DOM,找到<style>和/或<link rel=“stylesheet”>標籤,自己獲取 CSS,解析它,重寫它,然後將其添加回 DOM。

當然,更新 DOM 通常意味著瀏覽器必須重新經歷整個級聯、佈局、繪製和合成步驟。

渲染過程 Polyfill
使用 JavaScript 填充瀏覽器的渲染管道。 (查看大圖)

雖然必須完全重新呈現頁面可能看起來不會對性能造成太大影響(尤其是對於某些網站),但請考慮這可能發生的頻率。 如果你的 polyfill 的邏輯需要運行以響應諸如滾動事件、窗口大小調整、鼠標移動、鍵盤事件之類的事情——實際上任何時候都發生了任何變化——那麼事情就會變得明顯,有時甚至非常緩慢。

當您意識到當今的大多數 CSS polyfill 都包含自己的 CSS 解析器和自己的級聯邏輯時,情況會變得更糟。 而且因為解析和級聯實際上是非常複雜的事情,這些 polyfill 通常要么太大,要么太有問題。

更簡潔地總結一下我剛才所說的一切:如果你希望瀏覽器做一些不同於它認為應該做的事情(給定你給它的 CSS),那麼你必須想辦法通過更新和修改來偽造它自己的 DOM。 您無權訪問渲染管道中的其他步驟。

但是我為什麼要修改瀏覽器的內部渲染引擎呢?

對我來說,這絕對是整篇文章中最重要的問題。 所以,如果你到目前為止一直在瀏覽,請慢慢仔細地閱讀這部分!

看完最後一節,我相信你們中的一些人在想,“我不需要這個! 我只是在構建普通的網頁。 我並不想侵入瀏覽器的內部或構建一些超級花哨的、實驗性的或前沿的東西。”

如果您這麼想,那麼我強烈建議您退後一步,真正檢查您多年來用於構建網站的技術。 想要訪問和掛鉤到瀏覽器的樣式過程不僅僅是為了構建花哨的演示——它是為了讓開發人員和框架作者能夠做兩件主要的事情:

  • 標準化跨瀏覽器差異,
  • 發明或填充新功能,以便人們今天可以使用它們。

如果您曾經使用過諸如 jQuery 之類的 JavaScript 庫,那麼您已經從這種能力中受益了! 事實上,這是當今幾乎所有前端庫和框架的主要賣點之一。 GitHub 上五個最流行的 JavaScript 和 DOM 存儲庫——AngularJS、D3、jQuery、React 和 Ember——都做了很多工作來規範跨瀏覽器的差異,這樣你就不必考慮它了。 每個都公開一個 API,並且它可以正常工作。

現在,想想 CSS 及其所有跨瀏覽器問題。 即使是流行的 CSS 框架(例如 Bootstrap 和 Foundation)聲稱跨瀏覽器兼容性實際上並沒有規範跨瀏覽器錯誤 - 他們只是避免它們。 CSS 中的跨瀏覽器錯誤已不再是過去式。 即使在今天,使用 flexbox 等新的佈局模塊,我們仍面臨許多跨瀏覽器的不兼容問題。

底線是,想像一下,如果您可以使用任何 CSS 屬性並確定它在每個瀏覽器中都能正常工作,那麼您的開發生活會變得多麼美好。 想想你在博客文章中讀到的或在會議和聚會上聽到的所有新特性——比如 CSS 網格、CSS 捕捉點和粘性定位。 想像一下,如果您今天可以使用所有這些功能,並且以與原生 CSS 功能一樣高效的方式使用它們。 您需要做的就是從 GitHub 獲取代碼。

這是胡迪尼的夢想。 這就是工作組正在努力實現的未來。

因此,即使您不打算編寫 CSS polyfill 或開發實驗性功能,您也可能希望其他人能夠這樣做——因為一旦這些 polyfill 存在,每個人都會從中受益。

目前正在開發哪些 Houdini 功能?

我在上面提到,開發人員對瀏覽器渲染管道的訪問點非常少。 實際上,唯一的地方是 DOM,在某種程度上,還有 CSSOM。

為了解決這個問題,Houdini 工作組引入了幾個新規範,這些規範將首次允許開發人員訪問渲染管道的其他部分。 下圖顯示了流水線以及可以使用哪些新規範來修改哪些步驟。 (請注意,灰色的規格已計劃但尚未編寫。)

規格覆蓋
新的 Houdini 規範適合瀏覽器的渲染管道。 (查看大圖)

接下來的幾節簡要概述了每個新規範及其提供的功能類型。 我還應該注意,本文沒有提到其他規範; 如需完整列表,請參閱 Houdini 草稿的 GitHub 存儲庫。

CSS 解析器 API

CSS Parser API 目前未編寫; 所以,我所說的大部分內容很容易改變,但基本思想是它使開發人員能夠擴展 CSS 解析器並告訴它新的結構——例如,新的媒體規則、新的偽類、嵌套、@ @extends@apply

一旦解析器知道了這些新結構,它就可以將它們放在 CSSOM 中的正確位置,而不僅僅是丟棄它們。

CSS 屬性和值 API

CSS 已經有自定義屬性,而且,正如我之前所表達的,我對它們解鎖的可能性感到非常興奮。 CSS 屬性和值 API 使自定義屬性更進一步,並通過添加類型使它們更加有用。

將類型添加到自定義屬性有很多很棒的事情,但也許最大的賣點是類型將允許開發人員轉換和動畫自定義屬性,這是我們今天無法做到的。

考慮這個例子:

 body { --primary-theme-color: tomato; transition: --primary-theme-color 1s ease-in-out; } body.night-theme { --primary-theme-color: darkred; }

在上面的代碼中,如果將night-theme類添加到<body>元素中,那麼頁面上每個引用–primary-theme-color屬性值的元素都會慢慢地從tomato過渡到darkred 。 如果您今天想這樣做,則必須手動為這些元素中的每一個編寫轉換,因為您無法轉換屬性本身。

這個 API 的另一個有希望的特性是能夠註冊一個“應用掛鉤”,它為開發人員提供了一種在級聯步驟完成後修改元素上自定義屬性的最終值的方法,這對於 polyfill 來說可能是一個非常有用的特性。

CSS 類型的 OM

CSS Typed OM 可以被認為是當前 CSSOM 的第 2 版。 它的目標是解決當前模型的許多問題,並包含新的 CSS Parsing API 和 CSS Properties and Values API 添加的功能。

Typed OM 的另一個主要目標是提高性能。 將當前 CSSOM 的字符串值轉換為有意義類型的 JavaScript 表示將產生顯著的性能提升。

CSS 佈局 API

CSS Layout API 使開發人員能夠編寫自己的佈局模塊。 我所說的“佈局模塊”是指可以傳遞給 CSS display屬性的任何東西。 這將首次為開發人員提供一種與原生佈局模塊(例如display: flexdisplay: table )一樣高效的佈局方式。

作為一個示例用例,Masonry 佈局庫顯示了開發人員今天願意在多大程度上實現僅使用 CSS 無法實現的複雜佈局。 雖然這些佈局令人印象深刻,但不幸的是,它們存在性能問題,尤其是在功能較弱的設備上。

CSS Layout API 通過為開發人員提供一個registerLayout方法來工作,該方法接受一個佈局名稱(稍後在 CSS 中使用)和一個包含所有佈局邏輯的 JavaScript 類。 這是一個基本示例,說明如何通過registerLayout定義masonry

 registerLayout('masonry', class { static get inputProperties() { return ['width', 'height'] } static get childrenInputProperties() { return ['x', 'y', 'position'] } layout(children, constraintSpace, styleMap, breakToken) { // Layout logic goes here. } }

如果上述示例中的任何內容對您來說都沒有意義,請不要擔心。 主要關心的是下一個示例中的代碼。 下載masonry.js文件並將其添加到您的網站後,您可以像這樣編寫 CSS,一切都會正常工作:

 body { display: layout('masonry'); }

CSS 繪製 API

CSS Paint API 與上面的 Layout API 非常相似。 它提供了一個registerPaint方法,其操作與registerLayout方法一樣。 然後,開發人員可以在任何需要 CSS 圖像的地方使用 CSS 中的paint()函數,並傳入註冊的名稱。

這是一個繪製彩色圓圈的簡單示例:

 registerPaint('circle', class { static get inputProperties() { return ['--circle-color']; } paint(ctx, geom, properties) { // Change the fill color. const color = properties.get('--circle-color'); ctx.fillStyle = color; // Determine the center point and radius. const x = geom.width / 2; const y = geom.height / 2; const radius = Math.min(x, y); // Draw the circle \o/ ctx.beginPath(); ctx.arc(x, y, radius, 0, 2 * Math.PI, false); ctx.fill(); } });

它可以像這樣在 CSS 中使用:

 .bubble { --circle-color: blue; background-image: paint('circle'); }

現在, .bubble元素將以藍色圓圈作為背景顯示。 圓圈將居中並且與元素本身的大小相同,無論發生什麼。

工作集

上面列出的許多規範都顯示了代碼示例(例如registerLayoutregisterPaint )。 如果您想知道將代碼放在哪裡,答案就在工作集腳本中。

Worklet 類似於 Web Worker,它們允許您導入腳本文件並運行 JavaScript 代碼,這些代碼 (1) 可以在渲染管道中的各個點調用,並且 (2) 獨立於主線程。

Worklet 腳本將嚴格限制您可以執行的操作類型,這是確保高性能的關鍵。

複合滾動和動畫

雖然目前還沒有關於復合滾動和動畫的官方規範,但它實際上是更知名和備受期待的 Houdini 功能之一。 最終的 API 將允許開發人員在主線程之外的合成器工作集中運行邏輯,並支持修改 DOM 元素屬性的有限子集。 此子集將僅包括可以讀取或設置的屬性,而無需強制渲染引擎重新計算佈局或樣式(例如,變換、不透明度、滾動偏移)。

這將使開發人員能夠創建高性能的基於滾動和輸入的動畫,例如粘性滾動標題和視差效果。 您可以在 GitHub 上閱讀有關這些 API 試圖解決的用例的更多信息。

雖然還沒有官方規範,但 Chrome 已經開始了實驗性開發。 事實上,Chrome 團隊目前正在使用這些 API 最終會公開的原語來實現 CSS 捕捉點和粘性定位。 這太神奇了,因為這意味著 Houdini API 的性能足以讓新的 Chrome 功能在它們之上構建。 如果您仍然擔心 Houdini 不會像本地人一樣快,那麼僅憑這一事實就可以說服您。

為了看一個真實的例子,Surma 錄製了一個在 Chrome 內部版本上運行的視頻演示。 該演示模仿了 Twitter 本地移動應用程序中滾動標題的行為。 要了解它是如何工作的,請查看源代碼。

你現在可以做什麼?

如前所述,我認為每個建立網站的人都應該關心 Houdini; 這將使我們未來的生活變得更加輕鬆。 即使您從不直接使用 Houdini 規範,您幾乎肯定會使用構建在其之上的東西。

雖然這個未來可能不會立竿見影,但它可能比我們許多人想像的更接近。 所有主要瀏覽器供應商的代表都參加了今年早些時候在悉尼舉行的最後一次 Houdini 面對面會議,對於構建什麼或如何進行幾乎沒有分歧。

據我所知,這不是胡迪尼是否會成為一件事的問題,而是什麼時候,這就是你們所有人進來的地方。

瀏覽器供應商,就像其他構建軟件的人一樣,必須優先考慮新功能。 這種優先級通常取決於用戶對這些功能的渴望程度。

因此,如果您關心 Web 上樣式和佈局的可擴展性,並且如果您希望生活在一個可以使用新 CSS 功能而無需等待它們通過標準流程的世界中,請與您使用的瀏覽器的開發人員關係團隊,並告訴他們您想要這個。

您可以提供幫助的另一種方式是提供現實世界的用例——您希望能夠通過樣式和佈局來完成目前很難或不可能完成的事情。 GitHub 上的一些草稿都有用例文檔,您可以提交拉取請求來貢獻您的想法。 如果文檔不存在,您可以啟動一個。

Houdini 工作組(以及整個 W3C)的成員確實希望 Web 開發人員提供周到的意見。 大多數參與規範編寫過程的人都是從事瀏覽器工作的工程師。 他們本身通常不是專業的 Web 開發人員,這意味著他們並不總是知道痛點在哪裡。

他們依靠我們來告訴他們。

資源和鏈接

  • CSS-TAG Houdini Editor Drafts, W3C 所有Houdini 草稿的最新公開版本
  • CSS-TAG Houdini Task Force Specifications, GitHub 規範更新和開發的官方 Github 存儲庫
  • Houdini 示例,GitHub 代碼示例展示和試驗可能的 API
  • Houdini 郵件列表,W3C 提問一般問題的地方

特別感謝 Houdini 成員 Ian Kilpatrick 和 Shane Stephens 審閱本文。