通用優先 CSS:移動優先的新思維
已發表: 2022-03-10我認為可以肯定地說,Ethan Marcotte 的響應式 Web 設計對全世界的 Web 開發人員來說是一個受歡迎的啟示。 它引發了全新的設計思維浪潮和精彩的前端新技術。 經常被鄙視的 m dot 網站的統治也結束了。 在同一時代,幾乎同樣具有影響力的是 Luke Wroblewski 的 Mobile First 方法——建立在 Marcotte 令人印象深刻的基礎之上的堅實改進。
這些技術是大多數 Web 開發人員生活的基石,它們為我們提供了很好的服務,但可惜的是,時代在變,開發人員不斷迭代。 隨著我們提高方法的效率和項目要求變得更加複雜,新的挫折出現了。
通用優先之旅
我無法準確指出是什麼讓我改變了我編寫 CSS 的方式,因為這對我來說真的是一種自然的進步,幾乎是下意識地發生的。 回想起來,我認為這更像是我工作的開發環境的副產品。與我一起工作的團隊有一個很好的 SCSS 工作流程,其中包含一個漂亮的小 mixin,可以輕鬆地在我們的 CSS 聲明中添加斷點。 您可能使用了類似的技術。
這個奇妙的小 SCSS mixin 突然使編寫超細粒度的媒體查詢變得容易。 假設一個看起來像這樣的傳記塊:
.bio { display: block; width: 100%; background-color: #ece9e9; padding: 20px; margin: 20px 0; @include media('>=small') { max-width: 400px; background-color: white; margin: 20px auto; } @include media('>=medium') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }
圖。1。 具有級聯媒體查詢的典型移動優先
這很好用——我過去寫過很多這樣的 CSS。 然而,有一天我突然意識到,隨著設備寬度的增加覆蓋 CSS 聲明是沒有意義的。 為什麼要聲明一個 CSS 屬性,它只會在下面的聲明中被覆蓋?
這就是導致我開始編寫分區媒體查詢的原因,而不是像圖 1 中的示例那樣向上(或向下)級聯的更常見的媒體查詢方法。
我沒有編寫隨著屏幕尺寸增加而向上級聯的媒體查詢,而是開始創建有針對性的媒體查詢,將樣式封裝在所需的屏幕寬度上。 媒體查詢 mixin 將在這裡真正發揮作用。 現在我的 SCSS 媒體查詢開始看起來像這樣:
.bio { display: block; width: 100%; padding: 20px; margin: 20px 0; @include media('>=small', ' < medium') { max-width: 400px; margin: 20px auto; } @include media('>=medium', ' < large') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large', ' < huge') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }
圖 2。 劃分媒體查詢的示例
這種新方法對我來說更直觀,它減少了從前一個斷點重置樣式的次數,並且使 CSS 更易於閱讀。 更重要的是,它使媒體查詢以更重要的方式自我記錄。
儘管如此,我仍然不是 100% 滿意,似乎還有一個重大問題需要克服。
移動優先的問題
移動優先的問題在於,根據定義,您很可能必須在後續媒體查詢中覆蓋移動優先樣式。 這感覺有點像反模式。
所以 - 對我來說 - 答案很明顯:讓我們將媒體查詢劃分的想法推向其合乎邏輯的結論 - 我們還將移動特定樣式劃分為他們自己的媒體查詢。 我知道,我知道,這違背了我們多年來學到的共同慣例。 “移動優先”如此普遍,以至於它通常是招聘經理會問的“技能”問題之一。 因此,任何替代方案肯定都是錯誤的,不是嗎? 這通常是人們在一遍又一遍地首先說出移動時向我搖頭的部分。
好的,所以我們將打破移動優先的教條,將我們所有的樣式劃分為相關的媒體查詢。 我們現在剩下的是在 CSS 選擇器上聲明的純通用樣式,所有其他特定於設備的樣式都封裝在僅適用於相關屏幕尺寸的媒體查詢中。 我們現在有Generic First CSS :
.bio { display: block; width: 100%; @include media('>=0', ' < small') { padding: 20px; margin: 20px 0; } @include media('>=small', ' < medium') { max-width: 400px; margin: 20px auto; } @include media('>=medium', ' < large') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large', ' < huge') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }
圖 3。 通用優先 CSS 示例
是的,有更多的媒體查詢,但是,我認為這是一個好處,任何開發人員現在都可以查看這個 CSS 並準確地查看在每個屏幕尺寸上應用了哪些樣式,而無需挑選媒體的認知開銷 -查詢特異性。
這對於不熟悉代碼庫的人甚至未來的您來說都非常有用!
何時不划分
有時媒體查詢劃分是一種負擔,在某些情況下,舊的 >= 媒體查詢很好。 請記住,我們要做的只是避免屬性覆蓋。
開發工具 Bliss
編寫分隔的 Generic First CSS 的一個主要意外後果是您將從開發人員工具樣式面板中獲得的體驗。 如果沒有媒體查詢級聯,您現在將更清楚地了解應用了哪些樣式 - 您將不會有一個樣式面板,其中充滿了從覆蓋的媒體查詢規則中刪除的聲明 -噪音消失了! 這——對我來說——是 Generic First CSS 技術的最大好處之一。 它為 CSS 調試體驗帶來了一點額外的理智,這是物超所值的。 稍後再謝謝我。
性能影響
所以所有這些 Generic First CSS 的好處開始聽起來不錯,但我認為還有一個我認為需要解決的最後一個關鍵問題。 這是關於性能優化的主題。 現在我還不確定,但我有一種暗示,完全劃分的媒體查詢可能會對性能產生輕微的好處。
瀏覽器執行稱為計算樣式計算的渲染任務。 這是瀏覽器計算在任何給定時刻需要將哪些樣式應用於元素的方式。 此任務始終在初始頁面加載時執行,但也可以在頁面內容更改或發生其他瀏覽器操作時執行。 您可以提高流程速度的任何提升都將非常適合初始頁面加載,並且可能對您網站頁面的生命週期產生復合影響。
所以回到通用的第一個 CSS:是否存在與瀏覽器必須解決大量級聯媒體查詢的 CSS 特異性相關的性能問題?
為了回答這個問題,我設計了一個測試用例,可用於衡量任何速度優勢或確實存在的劣勢。
測試用例
測試用例由一個基本的 HTML 頁面組成,該頁面輸出“bio”塊 5000 次,每個塊的標記相同,但類略有不同(數字微分器),該塊的 CSS 也輸出 5000 次, 類名是唯一不同的地方。 輸出的 CSS 通過一個名為 CSS MQPacker 的工具進行管道傳輸,這有助於通過將特定媒體查詢的所有單獨實例合併為一個來顯著減少使用大量內聯媒體查詢的 CSS 的文件大小——這是一個很棒的工具,可能會受益最現代的 CSS 代碼庫——我通過測試項目 package.json 中的 npm 任務將它用作獨立的 cli 工具,您也可以將它用作 postcss 插件,非常方便!
第一個測試用例是移動優先級聯媒體查詢示例,第二個測試用例是 CSS 的通用第一個分區變體。 這些案例的 CSS 有點冗長,可能可以用更簡潔的術語來編寫,但它實際上只是作為測試論證的粗略示例。
該測試針對桌面 Google Chrome v70 中的每個 CSS 變體運行了 20 次,雖然不是大量數據,但足以讓我大致了解性能增益/損失。
我選擇使用的測試指標是:
- 總頁面加載時間
使用 <head> 開頭和 <body> 結尾處的 Performance API 標記檢查頁面加載時間的基本指標 - 重新計算樣式
開發工具性能窗格中的時間。 - 整體頁面渲染
開發工具性能窗格中的時間。
結果表(所有時間以毫秒為單位)
移動優先 | 通用優先 | ||||||
---|---|---|---|---|---|---|---|
加載時間 | 計算樣式 | 總渲染時間 | 加載時間 | 計算樣式 | 總渲染時間 | ||
1135 | 565.7 | 1953年 | 1196 | 536.9 | 2012 | ||
1176 | 563.5 | 1936年 | 1116 | 506.9 | 1929年 | ||
1118 | 563.1 | 1863年 | 1148 | 514.4 | 1853年 | ||
1174 | 568.3 | 1929年 | 1124 | 507.1 | 1868年 | ||
1204 | 577.2 | 1924年 | 1115 | 518.4 | 1854年 | ||
1155 | 554.7 | 1991 | 1177 | 540.8 | 1905年 | ||
1112 | 554.5 | 1912年 | 1111 | 504.3 | 1886年 | ||
1110 | 557.9 | 1854年 | 1104 | 505.3 | 1954年 | ||
1106 | 544.5 | 1895年 | 1148 | 525.4 | 1881 | ||
1162 | 559.8 | 1920 | 1095 | 508.9 | 1941年 | ||
1146 | 545.9 | 1897年 | 1115 | 504.4 | 1968年 | ||
1168 | 566.3 | 1882年 | 1112 | 519.8 | 1861 | ||
1105 | 542.7 | 1978年 | 1121 | 515.7 | 1905年 | ||
1123 | 566.6 | 1970 | 1090 | 510.7 | 1820 | ||
1106 | 514.5 | 1956年 | 1127 | 515.2 | 1986年 | ||
1135 | 575.7 | 1869 | 1130 | 504.2 | 1882年 | ||
1164 | 545.6 | 2450 | 1169 | 525.6 | 1934年 | ||
1144 | 565 | 1894年 | 1092 | 516 | 1822 | ||
1115 | 554.5 | 1955年 | 1091 | 508.9 | 1986年 | ||
1133 | 554.8 | 2572 | 1001 | 504.5 | 1812 | ||
平均 | 1139.55 | 557.04 | 1980 | 1119.1 | 514.67 | 1903.15 |
圖 6。 20 次測試運行測量移動優先與通用優先 CSS 的關鍵負載/渲染指標。
從我公認的小數據集來看,我最初的懷疑似乎是正確的。 平均而言,我看到樣式重新計算任務花費的時間減少了 42 毫秒,速度提高了 7.6%,因此整體渲染時間也減少了。 差異並不令人興奮,但它是一種改進。 我不認為數據集大到足以 100% 確定,並且測試用例有點不切實際,但我很高興沒有看到性能下降。
我很想看到將通用的第一方法應用於以移動優先方式編寫的現實世界現有代碼庫 - 之前的度量標準對於日常實踐會更加現實。
如果有人對如何在更廣泛的迭代中自動執行此測試有任何建議,請在評論中告訴我! 我想一定有一個工具可以做到這一點。
結論
回顧一下這種新開發方法的好處......
- 完全符合預期的 CSS,無需再猜測;
- 自記錄媒體查詢;
- 更好的開發工具體驗;
- 渲染速度更快的頁面。
我想我不是唯一一個支持以這種風格編寫 CSS 的人。 如果您已經採用了通用的第一思維方式,那就歡呼吧! 但如果沒有,我想你會非常喜歡它帶來的好處。 我個人從整潔的開發工具體驗中受益匪淺,這本身對許多開發人員來說是一個巨大的積極因素。 這種編寫媒體查詢的方式的自我記錄性質也將對您自己和更廣泛的團隊(如果您有的話)有益。 最後,這些好處不會讓您在性能方面付出任何代價,而且事實上已經證明可以帶來邊際速度提升!
最後一句話
像所有的開發方法一樣,它可能並不適合所有人,但我很自然地陷入了 Generic First CSS,我現在認為它是一種有價值的工作方式,它為我提供了移動優先的所有好處,並添加了一些積極的新功能前端開發的艱鉅工作,再簡單不過了。
資源
測試用例回購
如果您想啟動測試用例並自己嘗試一下,您可以在 GitHub 上找到它,我很樂意看到其他人的一些報告。
工具
- CSS MQPacker
- 包括媒體