本篇為 [FE302] React 基礎 - hooks 版本 這門課程的學習筆記。如有錯誤歡迎指正!
React 中的 style
在 React 中有很多種方式可以寫 CSS,主要又可分為下列三種:
一、inline-style 行內樣式
直接在 HTML 標籤內加入 style 屬性,例如 style={},但需注意下列幾點:
- inline style 裡面放的是 object
- 只能傳入該元素支援的 inline style
- 而不能傳入偽元素或 hover
- 因為是 JavaScript 程式碼,需改為駝峰式命名
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Title({ size }) { if (size === 'XL') { return <h1>hello!</h1> } return( <h2 style={{ color: 'blue', // 需改為駝峰式命名 textAlign: 'center' }}>hello!</h2> ) }
|
顯示結果:

二、使用 webpack 打包
在標籤加上 className 屬性(因為 class 是保留字),會被瀏覽器渲染成 css 中的 class,再透過 webpack 打包來引入該 css 檔案:
1 2 3 4 5 6 7 8 9 10
| import './App.css';
function App() { return ( <div className="App"> <Title /> </div> ); }
|
然後編輯 App.css 檔案:
1 2 3 4
| .App { text-align: center; background: yellowgreen; }
|
顯示結果:

三、使用 styled-components 套件
styled-components 是一個 library,也是目前的主流作法。用了 styled-components 套件之後,基本上就不需再直接寫 App.css 等檔案,之後會以此方法進行介紹。
首先安裝 styled-components 套件,然後運行程式:
1 2
| $ npm install --save styled-components $ npm run start
|
css 是透過標籤模板的寫法,在也就是在 “`” 反引號裡面寫入 css 樣式:
修改 App.js 檔案,直接在 style 後面接元素名稱,並在反引號中寫入 css 程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import styled from 'styled-components'; const Description = styled.p` color: red; padding: 20px; bottom: 1px solid #000; `
function App() { return ( <div className="App"> <Title /> <Description> // => 編譯後會變成 <p> 這是副標題 </Description> </div> ); }
|
可以想成 Description 就是有 style.p 的 component,而 React 會動態隨機產生 className,並加入設定好的 class:

以切出簡單的 TodoItem 為例
根據上述範例,其實就是在 styled 後面寫 css 程式碼,因此也可寫成 Sass 語法:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| const TodoItemWrapper = styled.div` max-width: 80%; margin: 5px auto; display: flex; align-items: center; justify-content: space-between; padding: 8px 16px; border: 1px solid #eee; ` const TodoContent = styled.div` color: #000; `
const TodoButtonWrapper = styled.div``
const Button = styled.button` padding: 4px; color: #232332; // 也可使用 Sass 語法 &:hover { color: red; } & + & { margin-left: 4px; } `
function App() { return ( <div className="App"> <TodoItemWrapper> <TodoContent>This is Todo</TodoContent> <TodoButtonWrapper> <Button>未完成</Button> <Button>刪除</Button> </TodoButtonWrapper> </TodoItemWrapper> </div> ); }
|
結果如下:

通常會把模板 TodoItem 獨立寫成 component,改寫後如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function TodoItem ({ size, content }) { return ( <TodoItemWrapper> <TodoContent size={size}>{content}</TodoContent> <TodoButtonWrapper> <Button>未完成</Button> <Button>刪除</Button> </TodoButtonWrapper> </TodoItemWrapper> ); }
function App() { const titleSize = "M" return ( <div className="App"> <TodoItem content={123} /> <TodoItem content={456} size="XL" /> </div> ); }
|
也可以在 TodoContent 傳入參數 props,傳入參數的程式碼需寫在 ${...} 裡面,而在括號內的 css 程式碼則要用反引號包住:
1 2 3 4 5 6 7
| const TodoContent = styled.div` color: #000; font-size: 12px; ${props => props.size === 'XL' && ` font-size: 20px; `} `
|
結果如下:

styled component 實戰
有關 styled component 套件的詳細功能可參考官方文件,接著要舉一些常用語法作為範例。
範例一:透過 styled() 繼承樣式
- 如果是對 styled component 進行 restyle,裡面寫的 css 程式碼會蓋過原本的樣式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const GreenButton = styled(Button)` background: green; color: #eee; `
function TodoItem ({ size, content }) { return ( <TodoItemWrapper> <TodoContent size={size}>{content}</TodoContent> <TodoButtonWrapper> <Button>未完成</Button> <GreenButton>刪除</GreenButton> </TodoButtonWrapper> </TodoItemWrapper> ); }
|
- 如果是對一般的 component 進行 restyle,則需要在 component 傳入 className,用來接收 BlackTodoItem 這個 class 屬性:
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
| function TodoItem ({ className, size, content }) { return ( <TodoItemWrapper className={className}> <TodoContent size={size}>{content}</TodoContent> <TodoButtonWrapper> <Button>未完成</Button> <GreenButton>刪除</GreenButton> </TodoButtonWrapper> </TodoItemWrapper> ); } const BlackTodoItem = styled(TodoItem)` background: #000; `
function App() { const titleSize = "M" return ( <div className="App"> <TodoItem content={123} /> <BlackTodoItem content={456} size="XL" /> </div> ); }
|

像 MEDIA QUERY 這類通用性高的程式碼,可以獨立放在 constants\style.js 檔案,以便重複使用:
1 2
| export const MEDIA_QUERY_MD = '@media screen and {min-width: 768px}' export const MEDIA_QUERY_LG = '@media screen and {min-width: 1000px}'
|
接著就可直接在 App.js 引入使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { MEDIA_QUERY_MD, MEDIA_QUERY_LG} from './constants/style';
const Button = styled.button` padding: 4px; color: #232332; font-size: 20px; ${MEDIA_QUERY_MD} { font-size 16px; }
${MEDIA_QUERY_LG} { font-size: 12px; } &:hover { color: red; } & + & { margin-left: 4px; } `
|
RWD 結果如下:

範例三:使用 Sass 向量變數
透過傳入 Global 參數,我們能使用 Sass 向量變數。
舉例來說,我們可在 index.js 引入 ThemeProvider:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { ThemeProvider } from 'styled-components';
const theme = { colors: { primary_300: '#ff7777', primary_400: '#e33e3e', primary_500: '#af0505', } }
ReactDOM.render( <ThemeProvider theme={theme}> <App /> </ThemeProvider>, document.getElementById('root') );
|
這樣就可以在 App.js 中取用這些變數:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const TodoContent = styled.div` font-size: 30px; color: ${props => props.theme.colors.primary_300};
${MEDIA_QUERY_MD} { font-size: 20px; color: ${props => props.theme.colors.primary_400}; }
${MEDIA_QUERY_LG} { font-size: 12px; color: ${props => props.theme.colors.primary_500}; } `
|
也可以把 TodoItem component 獨立成 TodoItem.js 這個檔案,並且 export :
1
| export default function TodoItem() {...}
|
並在 App.js 引入:
1
| import TodoItem from './TodoItem'
|
這麼做的好處就是,依照功能切割程式碼,能夠達到模組化,進而提高程式碼可讀性。
參考資料:
- [筆記] JavaScript ES6 中的模版字符串(template literals)和標籤模版(tagged template)
- [第二十一週] React 基礎:如何寫 CSS