[React] Styled-component
在使用 React 框架進行開發時,大致會有 3 種使用 CSS 檔案的方法
- 匯入 CSS 檔案
import "./example.css";
- inline-style
return <button style={{ backgroundColor: "red", fontSize: "24px" }}></button>;
css in js
現在主流的工具有styled-components和@emotion/css
這篇文章主要介紹 css in js 的工具之一: styled-components
安裝與匯入
npm i styled-components
原本假設已經有寫好的 scss 檔案,我們可以直接把副檔名改成.jsx
,然後再去判斷哪些 class 需要組件化。(這邊說明直接使用 scss 檔案是因為 styled-components 可以直接支援。)
一開始還是需要引入套件
import styled from "styled-components";
基本範例
我們以 styled-components 的官網首頁為例,可以看到白色的 GitHub 按鈕
這個按鈕的程式碼如下:
const Button = styled.a`
/* This renders the buttons above... Edit me! */
display: inline-block;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
width: 11rem;
background: transparent;
color: white;
border: 2px solid white;
/* The GitHub button is a primary button
* edit this to target it specifically! */
${(props) =>
props.primary &&
css`
background: white;
color: black;
`}
`;
製作出個 Button 的元件,在styled
之後可以插入不同的 HTML 元素,這邊為 a 元素,用反引號(backticks)將該元件的 SCSS 放入其中。因為是.jsx 檔,我們可以帶入上層元件的props
和使用 JavaScript 進行操作。
包裹元件
我以之前練習的按鈕元件作為範例,要將.scss 檔改為.jsx,並置換內容為 styled-components,可以參考button.styles.jsx
的變換。
按鈕分成一般按鈕、Google 登入按鈕、反置按鈕,他們的共同處是都是一般按鈕的延伸,故我們可以先建立一般按鈕,在透過style()
包裹住一般按鈕來新增屬性
export const BaseButton = styled.button`
// some css
`;
export const GoogleSignInButton = styled(BaseButton)`
// google button specific css
`;
export const InvertedButton = styled(BaseButton)`
// inverted button specific css
`;
通用的按鈕
可以參考按鈕元件裡面的button.component.jsx
,我們的目標是要回傳一個通用的按鈕,他會依據 props 的不同,回傳對應的按紐。
首先要引入三種按鈕,我們建立三種按鈕的類型分別為 base, google, inverted,然後建立一個getButton
的函式會回傳我們需要的按鈕元件
import {
BaseButton,
GoogleSignInButton,
InvertedButton,
} from "./button.styles";
export const BUTTON_TYPE_CLASSES = {
base: "base",
google: "google-sign-in",
inverted: "inverted",
}; // 外部引入的元件可以傳入需要的按鈕類型,ex: buttonType={BUTTON_TYPE_CLASSES.inverted}
const getButton = (buttonType = BUTTON_TYPE_CLASSES.base) =>
({
[BUTTON_TYPE_CLASSES.base]: BaseButton,
[BUTTON_TYPE_CLASSES.google]: GoogleSignInButton,
[BUTTON_TYPE_CLASSES.inverted]: InvertedButton,
}[buttonType]);
透過 getButton 函式來取得通用按鈕再回傳
const Button = ({ children, buttonType, ...otherProps }) => {
const CustomButton = getButton(buttonType);
return <CustomButton {...otherProps}>{children}</CustomButton>;
};
通用 google 按鈕使用範例: sign-in-form.component.jsx
<Button
type="button"
buttonType={BUTTON_TYPE_CLASSES.google}
onClick={signInWithGoogle}
children={"Google sign in"}
></Button>
引用其他 styled-components
可以參考購物車元件裡面的cart-dropdown.styles.jsx
,匯出了三種按鈕,並被引用在新建立的 styled-component 之中
import {
BaseButton,
GoogleSignInButton,
InvertedButton,
} from "../button/button.styles";
export const CartDropdownContainer = styled.div`
// ... other scss
${BaseButton},
${GoogleSignInButton},
${InvertedButton} {
margin-top: auto;
}
`;
SVG 元素
參考購物車圖案裡面的cart-icon.styles.jsx
,可以使用 SVG 的 style-component 的方法如下
import { ReactComponent as ShoppingSvg } from "../../assets/shopping-bag.svg";
export const ShoppingIcon = styled(ShoppingSvg)`
width: 24px;
height: 24px;
`;
Props 改變屬性
參考引導商品裡面的directory-item.styles.jsx
,每個 BackgroundImage 的圖片會由給入的 props 決定
export const BackgroundImage = styled.div`
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-image: ${({ imageUrl }) => `url(${imageUrl})`};
`;
css in js 全域變數
參考表格輸入裡面的form-input.styles.jsx
,透過 const 建立變數,並使用css
函式來去儲存固定的 css 給其他元件使用
import styled, { css } from "styled-components";
const subColor = "grey";
const mainColor = "black";
const shrinkLabelStyles = css`
top: -14px;
font-size: 12px;
color: ${mainColor};
`;
export const FormInputLabel = styled.label`
color: ${subColor};
font-size: 16px;
font-weight: normal;
position: absolute;
pointer-events: none;
left: 5px;
top: 10px;
transition: 300ms ease all;
${({ shrink }) =>
shrink &&
shrinkLabelStyles}// 常見的jsx寫法,shrink為真才會帶入shrinkLabelStyles
`;
匯入 FormInputLabel 如下,參考form-input.component.jsx
import { FormInputLabel, Input, Group } from "./form-input.styles";
...
<FormInputLabel shrink={otherProps.value.length}>
{label}
</FormInputLabel>
...
結論
css in js 的寫法主要可以透過 styled-components 的套件去撰寫,以前在檔案繁多時,常會有重複 class 命名的衝突問題,造成屬性重疊、畫面不如預期,透過此套件可以成功地的克服。由於此套件會隨機產生 class 的名稱,有時會造成溯源不易,我們可以參考莫大的文章,將 className 作為 props 帶入 styled-component,來達到指定的 class 名稱。如此一來,我們就能透過 js 來撰寫 css 了!