寫於 2016.07.05css
原文連接:Houdini: Maybe The Most Exciting Development In CSS You’ve Never Heard Of 更多譯文將陸續推出,歡迎點贊+收藏+關注個人專欄,未完待續……前端
你是否曾經想要使用一些特別的CSS特性,卻由於不曾獲得全部瀏覽器的支持而選擇放棄?又或者是,這些特性獲得了全部瀏覽器的支持,但總會伴隨着奇怪的bug,表現不一致甚至相互矛盾?若是這些事情都曾發生在你身上——我敢打賭——你應該關注一下Houdini。git
Houdini是W3C的一項新的任務,其宗旨在於解決上面所說的問題。它計劃經過提供一系列的API,使開發者得以自定義擴展CSS,並把這些樣式直接放入瀏覽器的渲染引擎中渲染出來。github
但這事實上到底意味着什麼?這真的是一個好主意嗎?以及它會如何幫助咱們開發現代的和麪向將來的頁面呢?web
在這篇文章中,我將嘗試回答上述問題。在此以前,明白當今存在什麼問題,以及須要作出什麼改變,是很是重要的。待會我將更加詳細地介紹Houdini是如何解決問題的,並將列出一些在目前開發過程當中遇到的酷炫的特性。文章的最後,我會提供一些實實在在的能讓咱們這些web開發者爲Houdini成爲現實的作法。api
每當我撰寫文章或者製做DEMO,用於展現一些新的CSS特性的時候,不可避免的總有人評論或者在Twitter留言說:「真是酷炫狂霸叼炸天!但糟糕的是在將來十年內咱們都沒法使用它們。」跨域
就像上面那些負能量滿滿又毫無建設性的評論那樣,我深覺得然。在歷史上,新特性的草案每每通過多年才被廣泛接受。正由於如此,而且在縱觀web的發展史後,讓新特性草案真正成爲CSS標準的惟一辦法就是讓其走一遍標準流程。瀏覽器
標準流程中的每一步面對這所謂的標準流程我無力反抗,但必須認可這浪費了大量的時間!bash
比方說,flexbox第一次被提議是在2009年,但開發者們仍然在抱怨直到如今都沒法使用它,由於只有少數瀏覽器支持這一特性。好在隨着大部分瀏覽器都支持自動更新,這個問題正在慢慢得以改善;然而,即便擁有着現代瀏覽器,從草案到成爲可用標準之間,依然存在着遲延。併發
有趣的是,這並不是是web開發中全部領域都出現的問題。讓咱們看看Javascript是怎麼作的:
Js中寫polyfill的步驟在這種狀況下,一個方案從構思到在生產環境使用,每每只不過幾天時間。個人意思是,在生產環境中我已經在使用async/await
方法了,即便這個方法未被任何一個瀏覽器所支持!
你也能夠感覺到這兩個社區的巨大情緒差別。在Javascript社區中,你會看到一些抱怨JS發展太快的文章。與之相反的,在CSS社區你會聽到一些感嘆,在可以真正使用以前,學習任何新的特性都是徒勞的。
乍這麼一想,寫CSS的polyfill彷佛是現成的答案。伴隨着優秀的polyfill,CSS將會發展得跟Javascript同樣快,對嗎?
很惋惜,這並無那麼簡單。爲CSS進行polyfill很是困難,更多的時候每每會毀掉全部的性能。
Javascript是一門動態語言,這意味着你能夠用JS去polyfill它自身。也正由於它動態的特性,它是很是易於擴展的。從另外一個角度來講,CSS幾乎不能被本身所polyfill。在某些狀況下,你能夠經過構建的方法實現CSS的polyfill(POSTCSS就是幹這個的);然而當你想要polyfill任何依賴於DOM結構的,或者某一元素樣式或位置的東西的時候,你不得不在客戶端運行你的polyfill邏輯。
不幸的是,瀏覽器很難實現這個需求。
下面的圖片展現的是瀏覽器對於一個HTML文檔從接收到渲染的基本過程。其中藍色區域就是Javascript有能力做出控制的步驟:
Javascript在瀏覽器渲染進程中的控制權這幅圖挺讓人沮喪的。做爲一個開發者,你沒法控制瀏覽器是如何將HTML和CSS解析爲DOM 和 CSS 對象模型(CSSOM)的;沒法控制整個渲染過程;沒法控制瀏覽器是如何選擇把元素渲染到DOM上面的,或者如何把內容填充到屏幕上展示給用戶;你也沒法控制瀏覽器是如何排版的。
你惟一可以徹底控制的只有DOM這一塊。在這種時候CSSOM是可用的;即使如此,引用Houdini網站的一句話,這是「蹩腳的,瀏覽器之間不一致的,缺少論證的特性。」
舉個例子,在現在瀏覽器中的CSSOM,不會告訴你跨域樣式表的規則,而且很容易就會拋棄掉它看不懂的CSS語法或者聲明——這意味着當你想要polyfill一些瀏覽器不支持的特性的時候,你沒法使用CSSOM。取而代之的,你只能手動遍歷DOM,找到<style>
或者(和)<link>
標籤,解析它,重寫它,再把它從新添加回DOM。
固然,更新DOM意味着瀏覽器將會完整地從新進行一遍及局、繪製、排版的重繪過程。
使用Javascript在瀏覽器渲染階段進行polyfill當完整地渲染頁面的時候也許並不會形成如此大的性能衝擊(尤爲對於一些網站來講),不怎麼需考慮發生的可能性。若是你的polyfill邏輯須要在事件響應中進行,好比滾動事件,窗口大小改變事件,鼠標移動事件,鍵盤事件——每時每刻都在發生這些事情的時候——性能方面的影響就會很是大,有時候甚至會卡頓,崩潰。
更糟糕的是,你會發現現在絕大多數的CSS polyfill都包含了它們本身的CSS解析器以及運行邏輯。與此同時,因爲解析和運行都是很是複雜的事情,因此這些polyfill要麼太大,要麼太容易有bug。
綜上所述,若是你但願瀏覽器去作一些它並不懂如何去作的工做(好比使用你的自定義CSS),那麼你必須假裝一些指令給瀏覽器,能夠經過手動更新和修改DOM來實現。除此以外你沒有任何辦法影響渲染過程當中的其餘階段。
對我來講,這個問題是這篇文章的關鍵。若是以前的內容你只是粗略瀏覽的話,那麼請認真仔細地閱讀接下來的這一段文字。
通過上一小節,我敢確定有部分讀者已經在想:「我不須要這個!我只是在製做普通的頁面而已。我並無準備把瀏覽器給黑了或者弄一些什麼創意啊,實驗啊,尖端產品之類的玩意兒。」
若是你這麼想,我強烈建議你回顧一下這些年來你在開發時所用技術的發展史。但願可以進入並修改瀏覽器樣式渲染過程的想法並不是爲了酷炫的demo——而是讓開發者或者框架更大的權利去作兩件主要的事情:
若是你曾經使用過諸如jQuery的Javascript庫,你已經從中獲益了!事實上,這正是現在幾乎全部前端庫和框架的賣點。Github上的五個最受歡迎的Javascript和DOM操做框架——AngularJS,D3,JQuery,React和Ember——它們全都爲瀏覽器兼容作了許多的工做,從而讓你根部不須要考慮瀏覽器兼容性。它們都經過向外提供的API就能直接被使用。
如今,讓咱們回到CSS以及它的跨瀏覽器兼容問題。即便是流行如Bootstrap和Foundation這兩個聲稱主打兼容性的CSS框架也沒法避免跨瀏覽器帶來的bug——它們只是避開了bug而不是解決它。同時,CSS跨瀏覽器帶來的bug不只僅是過去的事情。即時在今天,面對如flexbox之類的新特性,咱們仍然面臨着跨瀏覽器帶來的不一致性問題。
最後,想像一下若是你確信你能使用任何的CSS屬性,而且在不一樣瀏覽器中它們都能正常運行,你的開發體驗將會多麼溫馨。再設想一下,任何你在博客、大會上聽到的新特性——好比CSS grids,CSS snap points和sticky positioning,都可以經過某種方式像原生CSS同樣獲得完美運行,全部的一切只須要你從Github上覆制一段代碼,那將會多麼美好。
這正是Houdini的目標,這正是其組織所爲之奮鬥的將來。
所以,即便你並不打算寫CSS的polyfill或者開發一個實驗性質的新特性,你也極可能會但願別人去作這些——由於一旦這些polyfills被實現了,全部人都能從中獲益。
我在上文提到過,開發者僅有一點點權利去操做瀏覽器的渲染過程。確實,只有DOM和CSSOM可以被開發者操做。
爲了解決這個問題,Houdini小組提供了一些新的方法,第一次讓開發者得以進入到渲染過程當中的其餘步驟。下面的圖片展現的是渲染過程以及新的方法是如何被使用而修改當中的步驟的。(注意灰色部分的方法是計劃中的但仍在修改的。)
Houdini的新方法影響瀏覽器渲染過程的位置接下來的幾個小節將會簡要介紹每個新方法及其所擁有的功能。我要說明的是,仍然有一些新方法並未收錄到這篇文章當中,完整的方法列表請查閱GitHub repository of Houdini’s drafts。
CSS已經能夠自定義屬性了,正如我以前說的那樣,對於這個可能性的實現我表示很是興奮。CSS屬性-值API更是讓自定義屬性向前邁了一步,使其在添加屬性操做的時候更加實用。
有許多很棒的事情可以在自定義屬性中實現,但最大的賣點也許是可以讓開發者們在transition和animate當中,使用如今並不支持的自定義屬性。
想像一下下面的例子:
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 TYPED OM能被視爲當前CSSOM的2.0版本。其目標是爲了解決大量的關於現階段的規範,以及包括經過新的CSS解析API和CSS屬性-值API所帶來的問題。
另一個目標是爲了提高性能。把當前的CSSOM轉化成有意義的JS表達式,可以帶來大幅度的性能提高。
CSS LAYOUT API容許開發者自定義佈局模塊。「佈局模塊」指的是任何帶有CSSdisplay
屬性的元素。這將會是第一次爲開發者帶來性能媲美原生布局的方式,好比display: flex
和display: table
。
做爲實際使用的例子,Masonry layout library展現了開發者是多麼想要實現複雜的使人驚歎的佈局,卻沒法僅僅經過CSS來實現的情景。
不幸的是,他們被性能問題所困擾,尤爲是在一些能力稍差的設備上。
CSS LAYOUT API提供一個registerLayout
方法給開發人員,該方法接收一個佈局的名字做爲參數(一個接下來將會用在CSS當中的名字),以及一個包含着全部佈局邏輯的Javascript類。這裏展現的是一個經過registerLayout
API註冊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');
}
複製代碼
這個API很是像上文所說的LAYOUT API。它提供一個registerLayout
方法。開發者可以在 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
元素將會以一個藍色的圓爲背景被展現出來。無論發生什麼,這個圓都會等大居中地放置在元素當中。
前文的大部分的API都經過代碼塊來展現(好比registerLayout
和registerPaint
)。若是你想知道這些代碼應該放在哪裏運行,答案是放在WORKLET腳本中。
Worklets就像web workers,它們容許你引用腳本文件,而且在渲染過程的任意時刻運行當中的Javascript代碼,同時它也是獨立於主線程的。
Worklet腳本嚴格制約着你可以進行的操做,這正是保證高性能的關鍵。
即便到目前爲止仍然沒有關於composited scrolling and animation的官方說明,但這確實是Houdini衆多特性中最廣爲人知並不是常有但願實現的特性之一。最後的這個API容許開發者們把邏輯運行在脫離主線程的負責排版的worklet當中,同時也支持修改一個DOM元素子集的屬性值。這個子集僅僅包含了可以被讀寫的屬性,卻不會強迫渲染引擎去從新計算佈局或樣式(舉個例子,transform,opacity,scroll offset等。)
這將會容許開發者們去建立高性能的基於scroll-和input-的動畫,好比sticky scroll headers and parallax effects。你能夠在Github找到更多的關於這個API試圖去解決的問題的例子。
即便仍舊缺少官方文檔,可是有經驗的開發者們已經在Chrome瀏覽器中開始嘗試。事實上,Chrome團隊在最近已經開始基於這些最終將會發布的API來實現CSS snap points和sticky positioning。實在是振奮人心,由於這意味着Houdini API有着足夠的性能,以致於讓新的Chrome特性可以基於它們來實現。若是你還在擔憂Houdini不如原生那麼有效率,以上事實也許能夠在某種程度上打消你的疑慮。
看看真實的例子吧。Surma發佈了一個視頻,展現了一個運行在基於Chorme的瀏覽器內部的demo。Demo模仿了原生Twitter APP的用戶頭像變化行爲動畫。想知道它是怎麼實現的嗎?請看源碼。
根據上面提到的,我以爲任何一個web開發者都應該關心Houdini;它將會讓咱們的開發生涯更加方便。即便你從未直接使用過Houdini,但你頗有可能已經使用過基於它而開發出來的東西。
儘管這個將來並不會立刻到來,但極可能比咱們想象的都更加接近了。全部主流瀏覽器供應商的表明們在今年年初彙集在最後一次的Houdini面對面交流會上,會上對於如何去創建並發展Houdini的問題,已經基本達成了共識。
我能說的,不是Houdini是否可以最終實現的問題,而是你們可以在什麼時候,在何地可以參與進來的問題。
瀏覽器供應工商,就像任何一個軟件開發者同樣,必須重點發展新的特性。而且優先發展的每每是常常用戶強調須要的新特性。
因此,若是你關心web開發中樣式和佈局的擴展,若是你想要直接使用新的CSS特性而無需通過漫長的等待,請告訴你所使用的瀏覽器的開發成員。
另外一個你可以出力的地方,就是開發一些真實可用的案例——好比去實現一些在現在難以被實現的樣式或佈局。在Github上有幾個實際案例,你能夠提交併發佈pull request去貢獻你的想法。若是文檔不存在,你能夠新建一個。
Houdini小組的成員(通常來講叫W3C)很是期待來自開發者們的想法和建議。任何參與標準制定的都是瀏覽器開發工程師。他們一般不是專業的web開發者,這意味着他們並不是時刻清楚痛點在哪兒。
他們須要咱們去告訴他們。
特別鳴謝Houdini的成員Ian Kilpatrick和Shane Stephens審覈了這篇文章。
文章內容較多,未進行高質量的審覈校對,僅以原文爲準,若有錯漏歡迎指出。 不按期發佈開發體驗,學習心得,牆外乾貨,歡迎關注個人專欄。 感謝你的閱讀,我是Jrain,下次見!