css modules & styled-component

為什么要用css modules?

  1. 代碼只改動一處椰苟;
  2. 只應用在特定的組件抑月,不影響別的地方;

默認情況下舆蝴,所有類名和動畫名都在本地范圍內(nèi)的css文件谦絮。可以通過webpack或者Browserify來改變類名和選擇器名须误,以保證作用域挨稿。

這可以很好的解決css中的全局作用域問題捶枢。

// AccountInfo.less
.container {
    padding: 20px 0 0 40px;

    :global {
        .ant-col {
            font-weight: 700;
            text-align: left;

            &-5 > label {
                margin-left: 7px;
            }
        }

        .ant-form-item {
            margin-bottom: 14px;

            &-required::before,
            &-required::after,
            &-label > label::after {
                display: none;
            }
        }
    }
}
// 調(diào)用
import styles from './AccountInfo.less';

<div className={styles.container}></div>

編譯后:

<div class="src-biz-AccountManage-AccountInfo-AccountInfo__container--1Fy_N"></div>

composes 關(guān)鍵字

// colors.css

.display {
  color: red;
  font-size: 30px;
  line-height: 35px;
}

// element.css

.element {
    composes: display from "./colors.css"
}

composes關(guān)鍵字指明.normal包含所有來自.common的樣式听盖,類似于Sass里的@extends涂召。但是Sass通過重寫CSS選擇器來實現(xiàn)掸刊,而CSS模塊通過選擇哪個類輸出到JavaScript進行了改變试和。

不再需要BEM

BEM 是 Block蔓榄、Element呵恢、Modifier 的縮寫房午,利用不同的區(qū)塊方淤,功能以及樣式來給元素命名钉赁。這三個部分使用 與 連接(這里用兩個而不是一個是為了留下用于塊的命名)。命名約定如下:

.block {}

.block__element {}

.block--modifier {}

.block__element--modifier {}

BEM 的原則很簡單:一個 Block 代表一個對象(一個人携茂、一個登錄表單你踩、一個菜單);一個 Element 是一個塊中作為特定功能的組件(一個幫助按鈕讳苦、一個登錄按鈕带膜、一個菜單項);一個 Modifier 是我們?nèi)绾伪硎緣K或元素的不同變化(一個女人鸳谜、一個帶有隱藏標簽的迷你登錄框膝藕、 footer 中一個不同的菜單)。

BEM優(yōu)點:

通過這種命名方式咐扭,HTML 層級結(jié)構(gòu)一目了然芭挽,組件功能清晰明朗滑废,而且不必使用過多的層級選擇器,在一定程度上能夠提高 CSS 的渲染速度袜爪。

  1. 代碼結(jié)構(gòu)更加清晰蠕趁;
  2. 規(guī)范化
  3. 利于團隊協(xié)作
BEM缺點:
  1. 命名過長問。

在 DOM 層級過深的情況下饿敲,會導致 CSS Class 冗長妻导,難以閱讀,所以使用 BEM 命名的層級一般不超過4層怀各。

  1. 無法根治樣式污染問題

在某些情況下,比如命名稍不注意導致重名术浪,就可能會出現(xiàn)樣式污染問題瓢对,使用第三方庫的情況下也有可能會產(chǎn)生命名沖突,導致樣式污染胰苏。

在構(gòu)建CSS Modules時硕蛹,有兩個好處:

  1. 易解析,類似type.display這樣的代碼硕并,對于開發(fā)者來說就像BEM-y的.font-size__serif--large法焰。當BEM選擇器變長時,可能更容易被理解倔毙。

  2. 本地作用域埃仪。我們在一個模塊中運用.big中的font-size屬性,也可以在另一個類中陕赃,去同時增加padding和font-size卵蛉。因為本地作用域的不用,他們不會存在沖突么库,甚至可以在一個module中引入2個樣式表傻丝。本地作用域的原因,構(gòu)建工具會給class加上不同的前綴作區(qū)分诉儒。這可以很好的解決css樣式中的特殊性問題葡缰。

styled-components

簡易上手的幾個Demo

const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

/* Adapt the colors based on primary prop */
const Button = styled.button`
  background: ${props => props.primary ? "palevioletred" : "white"};
  color: ${props => props.primary ? "white" : "palevioletred"};
  
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

render(
  <div>
    <Button>Normal</Button>
    <Button primary>Primary</Button>
  </div>
)
/* Extending Styles */
const Button = styled.button`
  color: palevioletred;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

/* A new component based on Button, but with some override styles */
const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

vs 「css in js」的好處

相比于React的 css in js, styled-components 有一個好處,就是對偽元素的定義忱反。

const Thing = styled.button`
  color: blue;

  ::before {
    content: '??';
  }

  :hover {
    color: red;
  }
`

css in js的寫法泛释,偽元素只能通過onMouseOver這樣的js方法去控制,增加代碼量缭受。

Theming in styled-components

styled-components提供Context這樣的機制胁澳,有一套主題的上下文組件可供使用ThemeProvider

import {ThemeProvider} from 'styled-components';

const provider = (
    <Provider store={store}>
        <ThemeProvider theme={theme}>
            <App />
        </ThemeProvider>
    </Provider>
);

ReactDOM.render(
    provider,
    document.getElementById('root')
);

styled-components還提供React hook米者,可以結(jié)合useContext 來獲取theme韭畸。

import { useContext } from 'react';
import { ThemeContext } from 'styled-components';

const MyComponent = () => {
  const themeContext = useContext(ThemeContext);

  console.log('Current theme: ', themeContext);
  // ...
}

Issues with specificity

// MyComponent.js
const MyComponent = styled.div`background-color: green;`;

// my-component.css
.red-bg {
  background-color: red;
}

// For some reason this component still has a green background,
// even though you're trying to override it with the "red-bg" class!
<MyComponent className="red-bg" />

上述例子中宇智,styled-components可以取得在全局class之上的優(yōu)先權(quán)。因為styled-components是在運行時加載到<head>標簽的尾部胰丁。因此它的樣式優(yōu)先級要高于類選擇器随橘。

Tips: 如果想要強行覆蓋組件內(nèi)的樣式,可以通過加大選擇器的權(quán)重來達到覆蓋樣式的效果锦庸。

/* my-component.css */
.red-bg.red-bg {
  background-color: red;
}

/* 或者 */
&& {
    background-color: red;
}

Tagged Template Literals(標簽模板字符串)

標簽模板字符串机蔗,是ES6的新特性。他們讓你可以自定義字符串差值規(guī)則甘萧,這也是我們可以使用styled-components的原因萝嘁。

// These are equivalent:
fn`some string here`
fn(['some string here'])


const aVar = 'good'

// These are equivalent:
fn`this is a ${aVar} day`
fn(['this is a ', ' day'], aVar)

本質(zhì)上來說,調(diào)用函數(shù) styled.button() 和使用 styled.button``幾乎是一回事扬卷!但是當你傳入?yún)?shù)時就會看到不同之處了牙言。

我們先創(chuàng)建一個簡單的函數(shù)用于探索:

const logArgs = (...args) => console.log(...args)

const favoriteFood = 'pizza'

logArgs(`I like ${favoriteFood}.`)
// -> I like pizza.

logArgs`I like ${favoriteFood}.`
// -> ["I like ", "."] "pizza"


可以看到,我們不再僅僅是得到了一個內(nèi)容為 "I like pizza" 的字符串怪得。

傳入?yún)?shù)的第一位仍然是數(shù)組咱枉,不過現(xiàn)在有了 2 個元素:

  • 位于插值左側(cè)的 I like,作為數(shù)組第一個元素徒恋;
  • 位于插值的右側(cè)的 .蚕断,是數(shù)組第二個元素。

插值內(nèi)容 favoriteFoor 成為了第二個傳入?yún)?shù)入挣。

如果我們插入不止一個變量,

const favoriteFood = 'pizza'
const favoriteDrink = 'obi'

logArgs`I like ${favoriteFood} and ${favoriteDrink}.`
// -> ["I like ", " and ", "."] "pizza" "obi"
Why is this useful?

對于 React 組件亿乳,你希望使用 props 值調(diào)整他們的樣式。比如我們通過傳入一個 primary 的 prop 值财岔,讓 <Button /> 組件變大一些风皿,像這樣:

<Button primary />

當你使用 styled-components 傳入一個插值函數(shù),我們其實就向組件傳入了一個 props匠璧,使用它就可以進行組件樣式調(diào)整桐款。

const Button = styled.button`
  font-size: ${props => props.primary ? '2em' : '1em'};
`;

現(xiàn)在如果 Button 是個基本按鈕(primary),就有 2em 大小的字體夷恍,否則為 1em魔眨。

// font-size: 2em;
<Button primary />

// font-size: 1em;
<Button />

再看一下logArgs 函數(shù)。我們傳入一個字符串酿雪。

logArgs(`Test ${() => console.log('test')}`)
// -> Test () => console.log('test')

logArgs`Test ${() => console.log('test')}`
// -> ["Test", ""] () => console.log('test')

我們在調(diào)用模版字符串時候遏暴,確實可以拿到函數(shù)了。為了測試指黎,我們來創(chuàng)建一個新的函數(shù)來執(zhí)行所有入?yún)⒌暮瘮?shù):

const execFuncArgs = (...args) => args.forEach(arg => {
  if (typeof arg === 'function') {
    arg()
  }
})

測試調(diào)用一波:

execFuncArgs('a', 'b')
// -> undefined

execFuncArgs(() => { console.log('this is a function') })
// -> "this is a function"

execFuncArgs('a', () => { console.log('another one') })
// -> "another one"

// 用模版字符串測試一波
execFuncArgs`Hi, ${() => { console.log('Executed!') }}`
// -> "Executed!"

execFuncArgs 的第二個參數(shù)其實就是一個函數(shù)朋凉,并且可以執(zhí)行這個函數(shù)。

styled-components的底層就是這么實現(xiàn)的醋安。在渲染時向插值函數(shù)中傳入 props杂彭,我們就可以基于 props 來定制樣式墓毒。

標簽模板字符串使得 styled-components API 得以實現(xiàn),沒有這個特性 styled-compnents 就不可能出現(xiàn)亲怠。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末所计,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子团秽,更是在濱河造成了極大的恐慌主胧,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件习勤,死亡現(xiàn)場離奇詭異踪栋,居然都是意外死亡,警方通過查閱死者的電腦和手機图毕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進店門己英,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吴旋,你說我怎么就攤上這事∠崞疲” “怎么了荣瑟?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摩泪。 經(jīng)常有香客問我笆焰,道長,這世上最難降的妖魔是什么见坑? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任嚷掠,我火速辦了婚禮,結(jié)果婚禮上荞驴,老公的妹妹穿的比我還像新娘不皆。我一直安慰自己,他們只是感情好熊楼,可當我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布霹娄。 她就那樣靜靜地躺著,像睡著了一般鲫骗。 火紅的嫁衣襯著肌膚如雪犬耻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天执泰,我揣著相機與錄音枕磁,去河邊找鬼。 笑死术吝,一個胖子當著我的面吹牛计济,可吹牛的內(nèi)容都是我干的茸苇。 我是一名探鬼主播,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼峭咒,長吁一口氣:“原來是場噩夢啊……” “哼税弃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起凑队,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤则果,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漩氨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體西壮,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年叫惊,在試婚紗的時候發(fā)現(xiàn)自己被綠了款青。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡霍狰,死狀恐怖抡草,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蔗坯,我是刑警寧澤康震,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布,位于F島的核電站宾濒,受9級特大地震影響腿短,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绘梦,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一橘忱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卸奉,春花似錦钝诚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秉继,卻和暖如春祈噪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尚辑。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工辑鲤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杠茬。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓月褥,卻偏偏與公主長得像弛随,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宁赤,可洞房花燭夜當晚...
    茶點故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內(nèi)容