本篇為 [FE302] React 基礎 - hooks 版本 這門課程的學習筆記。如有錯誤歡迎指正!
React 中的 style
在 React 中有很多種方式可以寫 CSS,主要又可分為下列三種:
一、inline-style 行內樣式
直接在 HTML 標籤內加入 style 屬性,例如 style={}
,但需注意下列幾點:
- inline style 裡面放的是 object
- 只能傳入該元素支援的 inline style
- 而不能傳入偽元素或 hover
- 因為是 JavaScript 程式碼,需改為駝峰式命名
function Title({ size }) {
if (size === 'XL') {
return <h1>hello!</h1>
}
return(
// 兩個大括號包住: JavaScript 程式碼 + 以物件形式
<h2 style={{
color: 'blue',
// 需改為駝峰式命名
textAlign: 'center'
}}>hello!</h2>
)
}
顯示結果:
二、使用 webpack 打包
在標籤加上 className 屬性(因為 class 是保留字),會被瀏覽器渲染成 css 中的 class,再透過 webpack 打包來引入該 css 檔案:
// 透過 webpack 來引入 css
import './App.css';
function App() {
return (
<div className="App">
<Title />
</div>
);
}
然後編輯 App.css 檔案:
.App {
text-align: center;
background: yellowgreen;
}
顯示結果:
三、使用 styled-components 套件
styled-components 是一個 library,也是目前的主流作法。用了 styled-components 套件之後,基本上就不需再直接寫 App.css 等檔案,之後會以此方法進行介紹。
首先安裝 styled-components 套件,然後運行程式:
$ npm install --save styled-components
$ npm run start
css 是透過標籤模板的寫法,在也就是在 “`” 反引號裡面寫入 css 樣式:
style.p`<css code>`
修改 App.js 檔案,直接在 style 後面接元素名稱,並在反引號中寫入 css 程式碼:
// 引入 styled-components 套件
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 語法:
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,改寫後如下:
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 程式碼則要用反引號包住:
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 程式碼會蓋過原本的樣式:
// 繼承 Button 這個 styled component
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 屬性:
// 繼承 TodoItem component,需要傳入 className
function TodoItem ({ className, size, content }) {
return (
<TodoItemWrapper className={className}>
<TodoContent size={size}>{content}</TodoContent>
<TodoButtonWrapper>
<Button>未完成</Button>
<GreenButton>刪除</GreenButton>
</TodoButtonWrapper>
</TodoItemWrapper>
);
}
// 繼承 TodoItem
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 實作 RWD
像 MEDIA QUERY 這類通用性高的程式碼,可以獨立放在 constants\style.js 檔案,以便重複使用:
export const MEDIA_QUERY_MD = '@media screen and {min-width: 768px}'
export const MEDIA_QUERY_LG = '@media screen and {min-width: 1000px}'
接著就可直接在 App.js 引入使用:
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:
import { ThemeProvider } from 'styled-components';
// 宣告 theme 變數
const theme = {
colors: {
primary_300: '#ff7777',
primary_400: '#e33e3e',
primary_500: '#af0505',
}
}
ReactDOM.render(
// 包住 App,並自訂 theme 屬性
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>,
document.getElementById('root')
);
這樣就可以在 App.js 中取用這些變數:
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 :
export default function TodoItem() {...}
並在 App.js 引入:
import TodoItem from './TodoItem'
這麼做的好處就是,依照功能切割程式碼,能夠達到模組化,進而提高程式碼可讀性。
參考資料:
- [筆記] JavaScript ES6 中的模版字符串(template literals)和標籤模版(tagged template)
- [第二十一週] React 基礎:如何寫 CSS