iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0

什麼是 CSS in JS

CSS in JS 解決了 CSS 命名的問題。

由於 component-based 的概念興起元件開發成為顯學,CSS in JS 提供了將樣式寫在元件中解決方案,也讓 CSS 需要從寫程式語言的角度去進行架構設計。

CSS in JS 不僅降低了維護難度也加速了開發速度,減少檔案切換和減少 class 命名錯誤等等問題,常見的 library 像是 css module、vanilla-extract、styled-components、styled-jsx (Next.js) 都非常好上手,更完整的將元件模組化並增加可重用性。

CSS in JS 缺點也要先說在前面,當需要 Server Render 時,相關的效能問題也需要一併考慮進去。

CSS in JS 像是把需要打扮的道具跟裝備直接跟使用的情境整合在一起,像是約會必勝穿搭、適合上班的裝容等等,在遇到相關情境時能夠輕鬆應對。

問題定義

  • 當元件在其他專案使用時,搬動除了必須搬完相對應的樣式檔,還必須放在正確的位置
  • 在專案最佳化時,樣式檔的引用順序須讓機器不會混淆,若將樣式檔拆太細,很可能發生 Code Splitting 時順序錯誤

優點與解決的問題

  • 解決 code spliting 可能出現的順序問題
  • 解決命名互相覆蓋,在撰寫樣式檔時也可以用 BEM 的命名規則避開或導入原子化樣式設計
  • 維護時不需要再去找到底被哪個樣式檔影響

缺點

  • RWD 不像 bootstrap 有格線系統那樣使用方便,所以可以額外導入 CSS 框架的格線系統混合使用
  • 較難做效果跟做覆蓋,不同狀態需要不同樣式時要再封裝一層
  • 語法的高亮、自動完成、防呆較不完整
  • 少了可以共用樣式的好處,像是 @extend@mixin 這樣的寫法,需把元件切的夠小,才會有一定程度的共用性

styled-components

styled-components 提供了在 JavaScript 中直接撰寫 CSS 的介面,意味著可以使用所有的 CSS 功能,切換主視覺要多用一個 <ThemeProvider>,傳入的 Props 差異太大時有點麻煩

import styled from "styled-components";

function Sample() {
  const StyledButton = styled.button`
    color: #ffffff;
    font-size: 20px;
    border-radius: 5px;
  `;
  return <StyledButton type="button">按鈕</StyledButton>;
}

套用主視覺配置

styled-components 也提供了主視覺的配置,透過 styled-components 提供的 ThemeProvider 在外層注入就可以在元件中直接取值。

  1. ThemeProvider 在外層注入視覺配置
import { ThemeProvider } from "styled-components";

const colors = {
  primary: "#c18e00",
  secondary: "#0086ff",
  dark: "rgba(0, 0, 0, 0.6)",
  muted: "rgba(0, 0, 0, 0.3)",
  light: "rgba(0, 0, 0, 0.18)",
};

const fontSizes = {
  xl: `${1.875 * base}rem`,
  lg: `${1.375 * base}rem`,
  md: `${1.25 * base}rem`,
  base: `${1 * base}rem`,
  sm: `${0.875 * base}rem`,
  xs: `${0.75 * base}rem`,
};

const theme = {
  colors,
  fontSizes,
};

class MyApp extends App {
  componentDidMount() {
    objectFitImages();
  }

  render() {
    const { Component, pageProps, reduxStore } = this.props;
    return (
      <ThemeProvider theme={theme}>
        <Provider store={reduxStore}>
          <Component {...pageProps} />
        </Provider>
      </ThemeProvider>
    );
  }
}
  1. 在元件中直接取值,甚至可以寫 Function 進行判斷,做出更多元的變化
const StyleWrapper = styled.div`
  background: ${({ color, reverse, theme: { colors } }) =>
    reverse ? colors[color] : "none"};
`;

function TestMessage({ color, reverse, message }) {
  return (
    <StyleWrapper color={color} reverse={reverse}>
      <span>{message}</span>
    </StyleWrapper>
  );
}

RWD

由於 styled-components 提供了 function 的撰寫模式,所以即使撰寫 RWD 的元件也很簡單。

function getWidthString(span) {
  let result;
  if (span) {
    result = `width: ${(span / 12) * 100}%`;
  }
  return result;
}

const Column = styled.div`
  float: left;
  width: 100%;
  /* stylelint-disable value-keyword-case */

  ${({ xs }) => (xs ? getWidthString(xs) : "width: 100%")};

  @media only screen and (min-width: 768px) {
    ${({ sm }) => sm && getWidthString(sm)};
  }

  @media only screen and (min-width: 992px) {
    ${({ md }) => md && getWidthString(md)};
  }

  @media only screen and (min-width: 1200px) {
    ${({ lg }) => lg && getWidthString(lg)};
  }
`;

styled-jsx

styled-jsx 則是 Next.js 內建支援的介面,基礎的功能也是差不多,就是寫法上稍微不太相同。

function Sample() {
  return (
    <>
      <style jsx>
        {`
          button {
            color: #ffffff;
            font-size: 20px;
            border-radius: 5px;
          }
        `}
      </style>
      <button type="button">按鈕</button>
    </>
  );
}

global 的樣式,RWD 之類的就可以訂在這邊。

export default () => (
  <div>
    <style jsx global>{`
      body {
        background: red;
      }
    `}</style>
  </div>
);

動態的寫法,跟 styled-components 可以做到判斷,但又沒有 function 那麼彈性。

const Button = (props) => (
  <button>
    Hello
    <style jsx>{`
      button {
        padding: ${"large" in props ? "50" : "20"}px;
        background: ${props.theme.background};
      }
    `}</style>
  </button>
);

CSS Modules

CSS Modules 是用來解決 CSS 重複命名需要時常處理衝突的問題,在 Create React App 中 CSS 命名規則為 {檔名}.module.css

main.module.css

.information {
  color: red;
}

page.module.css

.information {
  color: green;
}
import styleMain from "./main.module.css";
import stylePage from "./page.module.css";

export default function Test() {
  return (
    <div>
      <h1 className={styleMain.information}>App</h1>
      <h3 className={stylePage.information}>Test</h1>
    </div>
  );
}

在 Class 的命名用駝峰式會較佳,但沒有一定。

style["class-name"];
style.className;

上一篇
CSS 框架用了什麼設計模式 (6)
下一篇
九種亂寫 CSS 的方法 X 有比亂化妝好嗎 (8)
系列文
前端三分鐘 X 從把妹角度理解前後端如何和平相處30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言