不要再在JavaScript中寫 CSS了

2020-11-30 TechWeb

本文作者是 react-css-modules 和 babel-plugin-react-css-modules 的作者。並不是對 CSS in JavaScript: The future of component-based styling,或是使用樣式組件的反對,而是一種補充,web 開發者要了解自己的需求,明白自己使用 styled-components 的真正原因。

9 個謊言

CSS 不應隨意放置。許多項目選擇將樣式寫在 JavaScript 中的理由不對。本文列出了常見的誤解,以及解決問題的現存 CSS 方案。

本文的任何言論都沒有對某個項目或人進行人身攻擊的意思。styled-components 是 React 的目前趨勢,所以我將 styled-components 定義為「JavaScript 中的 CSS」。

styled-components 的發起人(Max Stoiber、Glen Maddern 以及所有的貢獻者)都很聰明、想法獨特,出發點也是好的。

為了完全透明,我還要指出我是 react-css-modules 和 babel-plugin-react-css-modules 的作者。

小紅帽

CSS 和 JavaScript 歷史

層疊樣式表(CSS)是為描述標記語言文檔的展現樣式而出現的。JavaScript 是為了組合圖片、插件等組件而創造的一種「膠水語言」。隨著發展,JavaScript 拓展、轉變,有了新的應用場景。

Ajax 的出現(2005)是一個重要的裡程碑。這時 Prototype、jQuery、MooTools 等庫已經吸引了大量的擁護者,共同解決後臺跨瀏覽器數據獲取問題。這又引發了新的問題:如何管理數據?

到了 2010 年,Backbone.js 出現,成為了應用狀態管理的行業標準。不久後,Knockout 和 Angular 雙向綁定的特點吸引了所有人。之後,React 和 Flux 出現,開啟了單頁應用(SPA)的新紀元,組件構造應用。

那麼 CSS 呢?

借用 styled-components 文檔中的話:

純 CSS 的問題在於它產生的那個時代,網站由文檔組成。1993 年,網站產生,主要用於交換科學文獻,CSS 是設計文獻樣式的解決方案。但是如今我們構建的是豐富的、面向用戶的交互應用,而 CSS 並不是為此而生的。

我不這麼認為 。

CSS 已經發展到可以滿足現代 UI 的需求了。過去十年中出現的新特性數不勝數(pseudo-classes、pseudo-elements、CSS variables、media queries、keyframes、combinators、columns、flex、grid、computed values 等等)。

從 UI 的角度看,「組件」是文檔中一個獨立的片段(<button /> 就是個組件)。CSS 被設計用來樣式化文檔,包括所有組件。問題在哪?

俗話說:「工欲善其事必先利其器」。

Styled-components

styled-components 可以用標記模板字面量在 JavaScript 中寫 CSS。這樣就省去了組件和樣式間的匹配 ——組件由細粒度的樣式結構組成,比如:

import React from 'react';  import styled from 'styled-components';  // Create a <Title> react component that renders an <h1> which is  // centered, palevioletred and sized at 1.5em  const Title = styled.h1`    font-size: 1.5em;    text-align: center;    color: palevioletred;  `;  // Create a <Wrapper> react component that renders a <section> with  // some padding and a papayawhip background  const Wrapper = styled.section`    padding: 4em;    background: papayawhip;  `;  // Use them like any other React component – except they're styled!  <Wrapper>    <Title>Hello World, this is my first styled component!</Title>  </Wrapper>  

結果:

Live demo(https://www.webpackbin.com/bins/-KeeZCr0xKfutOfOujxN)

styled-components 目前是 React 的 趨勢 。

我們要理清一件事情:styled-components 只是 CSS 層面的高度抽象。它只是解析定義在 JavaScript 中的 CSS,然後生成對應 CSS 的 JSX 元素。

我不喜歡這個趨勢,因為存在很多誤解。

我在 IRC、Reddit 和 Discord 上調查了大家使用 styled-components 的原因,整理了一份選擇使用 styled-components 常見原因的列表 。我稱之為 myths。

Myth #1:避免全局命名空間和樣式衝突

我把這條算作 myth 是因為它聽起來就像之前這些問題沒有得到解決一樣。CSS Modules、Shadow DOM 還有很多命名協議(比如 BEM)已經早就在社區中解決了這個問題。

styled-components(就像 CSS modules)只是替人完成了命名的任務。人總會犯錯,計算機犯錯少點而已。

但就本身而言,這並不是使用 styled-components 的好理由。

Myth 2:styled-components 可以簡明代碼

通常伴隨著如下的例子:

<TicketName></TicketName>  <div className={styles.ticketName}></div>  

首先——關係不大。差異基本可以忽略。

其次,說的也不對。字符數量取決於樣式命名。

<TinyBitLongerStyleName></TinyBitLongerStyleName>  <div className={styles.longerStyleName}></div>  

這同樣適用於本文之後的構造樣式(Myth 5:給組件設置條件樣式更簡單)。styled-components 只是在多數基本組件的情況下稍勝一籌。

Myth 3:styled-components 使人更關注語義化

前提就不對。樣式和語義化代表著不同的問題,需要不用的應對方案。引用 Adam Morse(mrmrs)的話:

內容語義化和視覺樣式 沒有半點關係。當我用樂高建造東西時,我從來不會想「這是引擎的一部分」,我想著「這是個 1×4 的藍色樂高,我用來隨便做什麼都行」。不論水下潛水基地還是飛機——我清晰地知道怎麼用這個樂高塊。

– http://mrmrs.io/writing/2016/03/24/scalable-css/

(強烈建議讀一讀 Adam 關於 可拓展 CSS 的文章)

我們還可以舉個例子看看兩者是否相關。

示例:

<PersonList>    <PersonListItem>      <PersonFirstName>Foo</PersonFirstName>      <PersonLastName>Bar</PersonLastName>    </PersonListItem>  </PersonList>  

語義化是要使用正確的標籤構造標記。你能知道這些組件會渲染成什麼 HTML 標籤嗎?不,你不知道。

和下面這段代碼比較下:

<ol>    <li>      <span className={styles.firstName}>Foo</span>      <span className={styles.lastName}>Bar</span>    </li>  </ol>  

Myth 4:拓展樣式更容易

v1 版本可以用 styled(StyledComponent) 拓展樣式;v2 引進了 extend 方法來拓展已存在的樣式,比如:

const Button = styled.button`    padding: 10px;  `;  const TomatoButton = Button.extend`    color: #f00;  `;  

這挺好。但是你可以在 CSS 中完成(或者使用 CSS 模塊組合 或 SASS 繼承混合 @extend)。

button {    padding: 10px;  }  button.tomato-button {    color: #f00;  }  

難道不比 JavaScript 簡單?

Myth 5:給組件設置條件樣式更簡單

這點是說你可以根據組件屬性給組件設置樣式,比如:

<Button primary />  <Button secondary />  <Button primary active={true} />  

這在 React 中很有用。畢竟組件行為就是由屬性控制的。給屬性值直接綁定樣式有意義嗎?可能吧。但是來看看組件的實現代碼:

styled.Button`    background: ${props => props.primary ? '#f00' : props.secondary ? '#0f0' : '#00f'};    color: ${props => props.primary ? '#fff' : props.secondary ? '#fff' : '#000'};    opacity: ${props => props.active ? 1 : 0};  `;  

利用 JavaScript 按條件創造樣式表是挺強大的,但是這也意味著樣式難以理解,對比以下 CSS:

button {    background: #00f;    opacity: 0;    color: #000;  }  button.primary,  button.seconary {    color: #fff;  }  button.primary {    background: #f00;  }  button.secondary {    background: #0f0;  }  button.active {    opacity: 1;  }  

這樣 CSS 更簡短(229 VS 222 字符),(個人認為)也更容易理解。此外,還可以用預處理器使 CSS 分組、更短:

button {    background: #00f;    opacity: 0;    color: #000;        &.primary,    &.seconary {      color: #fff;    }    &.primary {      background: #f00;    }    &.secondary {      background: #0f0;    }    &.active {      opacity: 1;    }  }  

Myth 6:有利於代碼組織

有些人告訴我他們喜歡 styled-components,因為它可以讓樣式和 JavaScript 在一個文件中。

我理解同一組件有許多文件很煩,但是把樣式和標記塞進一個文件的方法很糟糕。這樣不僅版本控制難以回溯,而且所有組件都需要滾動很長一段距離,而不是簡單地點下按鈕。

如果一定要把 CSS 和 JavaScript 放在一個文件中, 可以考慮使用 css-literal-loader。它可以在 build 時用 extract-text-webpack-plugin 提取 CSS,用標準 loader 配置處理 CSS。

Myth 7:DX 很方便,這工具太棒了!

很明顯你沒用過 styled-components。

一旦樣式寫錯了,整個 app 會崩潰,並輸出長長的調用棧錯誤(v2 更奇葩)。相比之下,CSS 「style error」 只是元素渲染地不對而已。 元素沒有 className,所以調試時不得不去對比 React 元素樹和 DevTools DOM 樹(v2 可以用 babel-plugin-styled-components 定位)。 沒有語法檢查(有一款 樣式檢查插件 正在開發中)。 不合法的樣式會被忽略(比如:clear: both; float left; color: #f00; 不會報 error 或 warning,只能祈禱調試好運了,即使看了 styled-components 源碼,還是花了我 15 分鐘查看調用棧。最後我在聊天中把代碼粘出來尋求幫助,才有人提醒是少了:。你注意到了嗎?) 支持語法高亮、代碼補全以及其它 IDE 細節的 IDE並不多。如果你在金融或政府機構工作,很可能無法使用 Atom IDE。

Myth 8:性能更好,bundle 更小

事實是,styled-components 無法提取靜態 CSS 文件(比如使用 https://github.com/webpack-contrib/extract-text-webpack-plugin)。這意味著瀏覽器無法開始解釋樣式直到 styled-components 解析、加載到 DOM上。 缺少文件分離意味著無法分開緩存 CSS 和 JavaScript。 所有樣式化的組件都會額外包裝一層 HoC。這是不必要的性能損耗。因為類似的結構缺陷,我終止了 https://github.com/gajus/react-css-modules(但創建了 https://github.com/gajus/babel-plugin-react-css-modules)。 因為 HOC,如果在服務端渲染,會導致標記文檔大很多。 有 keyframes, 我也不需要用動態樣式值做動畫。

Myth 9:它可以開發響應式組件

這說的是依據環境給組件設置樣式的能力,比如父容器偏移量、子元素數量等。

首先,styled-components 和響應式沒什麼關係。這已經超出了這個主題的範圍。這種情況最好直接設置組件的 style,以避免額外的成本。

但是,元素查詢是個有趣的問題,也逐漸成為 CSS 中的一個高熱話題,主要是 EQCSS 等類似項目。元素查詢和 @media queries 在語法上很相似,只是元素查詢操作具體某些元素。

<a href="http://www.jobbole.com/members/feiguohai46">@element</a> {selector} and {condition} [ and {condition} ]* { {css} } 

{selector} 是 CSS 選擇器對應著一或多個元素。例如:#id 或 .class

{condition} 由尺寸和值組成。

{css} 可以包含:任何合法的 CSS 規則。(例如:#id div { color: red })

元素查詢可以用 min-width、max-width、min-height、max-height、min-characters、max-characters、min-children、max-children、min-lines、max-lines、min-scroll-x、max-scoll-x 等 (詳見 http://elementqueries.com/)條件給元素設置樣式。

總有一天類似 EQCSS 的內容也會出現在 CSS 標準中的(希望如此)。

等下!

大部分內容都長期有效,無論是社區、React 變更或 styled-components 本身。但意義何在?CSS 已被廣泛支持,有大量的社區,也確實行之有效。

本文的目的並不是阻止讀者在 JavaScript 中使用「CSS」或是 styled-components。styled-components 一個很棒的使用場景是:更好的跨平臺支持性。不要因為錯誤的理由使用它。

那麼我們應該用什麼呢?

使用 Shadow DOM v1 還為時尚早(51% 支持率)。CSS 應遵循命名協議(建議 BEM),如果擔心類名衝突(或懶得用 BEM),可以用 CSS modules。如果你在開發 React web,考慮用 babel-plugin-react-css-modules。如果在開發 React Native,styled-components 更好。

點讚 0

相關焦點

  • css 表單效果,純div+css實現
    純div+css實現,大家可以參考下,也許有一些問題,大家可以修正下<!www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>by 阿會楠 2008-12-4</title><script language="javascript
  • JavaScript在Avant瀏覽器中的妙用
    經常使用IE的朋友,可能喜歡在地址欄中輸入一些Javascript代碼來實現一些特定的功能。比如,測試一段Javascript代碼是否正確,放大網頁,提取網頁中的所有圖片,等等。  用過Avant瀏覽器的朋友,也一定會注意到Avant的地址欄中不能運行Javascript代碼。但是我們卻可以通過更簡單的方法在Avant中實現上面的功能。
  • 手把手教你打造一個純CSS圖標庫
    推廣 | 令人窒息的獎品等你―2016最權威的全球開發者調研 來,幹了這碗安利 寫這篇文章的目的其實就是為了安利一下我的圖標庫:iconoo,所以,開門見山,star吧少年少婦們!
  • What is super() in JavaScript?
    小夥伴們大家好,今天我們來說一下ES6中的super關鍵字,這次我嘗試了一種新的寫文形式:中英文結合
  • css3 box-sizing屬性筆記
    box-sizing屬性定義及用法box-sizing屬性是css3中新增的屬性,允許你以某種方式定義某些元素,以適應指定區域(假如您需要並排放置兩個帶邊框的框,可通過將 box-sizing屬性設置為"border-box",這可令瀏覽器呈現出帶有指定寬度和高度的框,並把邊框和內邊距放入框中
  • 經典 用CSS實現表單form布局
    以下為引用的內容:style type="text/css" label{float: left;width: 80px;}form{margin:0px}input{width: 180px;border:1px solid #808080}
  • 第41節 Document文檔節點-Javascript
    name特性的<a>元素;這個屬性已經從Web標準中刪除了,但瀏覽器還支持,所以儘量不要再使用;<!,返回一個HTMLAllCollection,包含了頁面上的所有元素;已從Web標準中刪除,但是瀏覽器都支持,但建議不要使用;與document.getElementsByTagName(「*」)得到的結果基本相同的;console.log(document.all); // HTMLAllCollectionconsole.log(document.getElementsByTagName
  • 16進位透明度 css_css 16進位顏色透明度 - CSDN
    16進位的透明顏色cssStandard CSS color keywords are limited to 149 named shades; the hexadecimal (or 「hex」) color method has access to the full RGB
  • JavaScript:詳解Base64編碼和解碼
    Base64是最常用的編碼之一,比如開發中用於傳遞參數、現代瀏覽器中的<img />標籤直接通過Base64字符串來渲染圖片以及用於郵件中等等。Base64編碼在RFC2045中定義,它被定義為:Base64內容傳送編碼被設計用來把任意序列的8位字節描述為一種不易被人直接識別的形式。
  • 25條CSS製作網頁編寫的提醒及小技巧整理
    important;屬性名:A}4、如果一組要嵌套的標籤之間需要些間距的話,那就留給位於裡面的標籤的margin屬性吧,而不要去定義位於外面的標籤的padding5、li標籤前面的圖標推薦使用background-image,而不是list-style-image。6、IE分不清繼承關係和父子關係的差別,全部都是繼承關係。
  • get神技能:用CSS控制div便可畫出超美聖誕樹
    html與css學習過程中會遇到各種各樣的問題,當然也有很多有趣的實戰應用,今天大鵬與大家分享如何用css來控制div畫三角形,進一步畫出聖誕樹。【注意】可以在寫完之後再重新重命名為.html文件。margin-left: 100px; } #tri2{ width: 0px; height: 0px; border-top: 200px solid white; border-right: 200px solid white; border-bottom: 200px solid green; border-left: 200px solid white; } 【畫樹幹】再加入一個
  • HTML+CSS實戰系列——盒模型製作
    如果把盒子模型看作是一個生活中的快遞,那麼內容部分等同於你買的實物,內邊距等同於快遞盒子中的泡沫,邊框等同於快遞盒子,外邊距等同於兩個快遞盒子之間的距離。你也可以在一個屬性中設置邊框。不同的瀏覽器內核對網頁編寫語法的解釋也有不同,因此同一網頁在不同的內核的瀏覽器裡的渲染(顯示)效果也可能不同,這也是網頁編寫者需要在不同內核的瀏覽器中測試網頁顯示效果的原因。
  • 傳說中的痘印橡皮擦,修麗可與CSS析顏士,平價代替存在嗎?
    圖源網絡保溼抗氧化抗炎成分:甘油、透明質酸鈉、黃瓜果提取物、麝香草花葉提取物鎮靜消炎成分:(以下這幾種成分是美版色修和css美版色修國內無渠道購買,如果找不到良心代購的妹子還是不要選擇美版色修。總體感受:三款精華小野都分別使用了一個月,都是單獨使用,沒有搭配其他水乳等產品。使用順序是美版加強至修麗可色修至析顏士色修,都用到空瓶。
  • 精美JS、CSS動畫時鐘設計、實現與實例分析
    前端頁面布局結構描述如下圖所示:前端布局HTML文件源碼在前端布局中我們使用clock頁面靜態效果展示如下:帶時針前端頁面靜態效果在初始時刻所有指針全部重疊,在完成前端設計之後即可使用javascript
  • 不要再嘲笑「山東情婦館」了,這幾個字寫得很不錯
    不要再嘲笑「山東情婦館」了,這幾個字很不錯第一次看到山東博物館這五個字是在求學的時候,那時候覺得這幾個字並沒有什麼不一樣的地方,只是帶著觀賞的心態去辨識了一下,和我們學校的圖書館的「館」字寫法和這個館字一模一樣,都是用草書寫成,在本校看圖書館這幾個字沒有什麼特別之處,久而久之,也就習慣了
  • 手把手 | 30行JavaScript代碼,教你分分鐘創建神經網絡
    下圖中的圓圈就代表一個sigmoid神經元。它的輸入值是5,輸出值是1。箭頭則代表的是神經元的突觸,用來連接神經網絡中其它層的神經元。為什麼會有一個紅色的數字5呢?它是連接到神經元的三個突觸(左邊3個箭頭)的值之和。在最左邊,我們看到有兩個值與所謂的偏差值進行了加法運算。數值1和0是綠色的,而偏差值-2是棕色的。
  • 不要再寫小貓、小狗了,沒有新意。看看名家筆下的動物怎麼寫
    這個夏天,我們來篇寫動物的文章吧,不要只想到小狗、小貓,如果多留意身邊的環境,你會發現大自然裡還有更多可愛、但又容易被你忽視的小動物喲。一起來欣賞名家汪曾祺老先生筆下的動物吧。是什麼呢?問作者:寫這些昆蟲什麼意思?