styled-components

Why ?

  • CSS-in-JS
    • 不需去處理一些 css 相關的 config set up (e.g. webpack)
  • Component with styling
    • style 與 component 綁在一起, 且可以透過 props 來動態變動 style
  • 程式可讀性提昇
    • 不用去寫一堆 className 嵌在 component 上

實作原理

語法

運作

  • 當建立一個 styled component 時
1
2
3
4
const Text = styled.div`
position: relative;
color: red;
`;

會透過 hash function 先產生一個 componentId, 來記錄這個 component

  • 實例化 一個 styled component 時
1
return <Text> Hello styled component </Text>;

會透過 componentId + style string (e.g. ‘position: relative;\ncolor: red;’) 作為 hash input, 產生 className, 且實例化後的 styled component 會自帶這個 className 屬性

  • 所有 styled-component 產生出來的 className, 都會 attach 在 html <style> tag 中
1
2
3
4
5
6
<style>
.hHskdj {
position: relative;
color: red;
}
</style>
  • 如此一來, styled component 即可對應到他的 style
  • 需要注意一下

styled-components generates an actual stylesheet with classes, and attaches those classes to the DOM nodes of styled components via the className prop. It injects the generated stylesheet at the end of the head of the document during runtime.

因此當我們在加工自己寫的 component 時, 需要另外補上 className 這個 props

1
2
3
4
5
// MyText 夾帶著 styled factory 產生的 hash class 在 className 這個 props 上, 內層的 div 需要綁上這個 className 才會有效果 .
const MyText = (props) => (<div className={props.className}>Hi!</div>);
const styledMyText = styled(MyText)`
color: red;
`;

Usage tips

styled-components’ selector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const Wrapper = styled.div`
background: red;
`;

const Text = styled.div`
color: white;

// 1.
& {
border-top: 2px solid black;
}

// 2.
${Wrapper} {
border-right: 2px solid black;
}

// 3.
${Wrapper} & {
border-bottom: 2px solid black;
}

// 4.
${Wrapper} * {
border-left: 2px solid black;
}
`;
  • & : Reference 到自己 (也就等於自己的 class name)
  • ${Other styled component} : Reference 到別的 styled-components (也就等於他人的 class name)
  • 上述例子中,只有 1, 3 會有效果,下面說明 1~4 分別會在 <style> tag 中建立什麼,假設 Text 的 class 為 .123, Wrapper 的 class 為 .456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.
.123 {
border-top: 2px solid black;
}

2.
.123 .456 {
border-right: 2px solid black;
}

3.
.456 .123 {
border-bottom: 2px solid black;
}

4.
.456 .123 * {
border-left: 2px solid black;
}
  • 結論
    • 子無法 Ref 到上層 Component 去設定其 style (見 2.)
    • 子無法先 Ref 到上層 Component 去設定其下層的 style (見 4.), 除非其中會 Ref 到該子 (見 3.)

Peformance issue

產生過多的 class in style tag

發生情境

假設有一個 styled component, 其 width 會隨著傳入的 width props 不同而改變

1
2
3
4
const Row = styled.div`
width: ${props => props.width}px;
height: 100px;
`;

而實例化的 Row, 其 width props 會隨著時間動態改變(假設透過 setInterval 一直去改變其 width 之類的), 這樣就會產生無限個 class, attach 在 <style> tag 裡面, 但實際上仍然是同一個元素, 差別只在 width 不同

  • 無限個 class
    • 會產生無限個 CSSOM 節點, 對 Memory 是個負擔

Prevent

use the style attr for all dynamic styles with undermined number of results

1
2
3
4
5
6
7
const Row = styled.div.attrs({
style: ({ width }) => ({
width: `${width}px`,
}),
})`
height: 100px;
`;

上面例子實體化的 styled component DOM 會夾帶 inline style - width, 當動態去改變 width 時, 會直接去更動 inline style width 的值, 並不會去產生新的 class 在 style tag 裡 => don’t generate bunch of CSS rules which only differ by one number so CSSOM is smaller and memory consumption drops

參考文獻