借助 Webpack 的力量,我們可以在 SPA 大量使用 JS 開發的環境下,一樣使用 JS 的形式來 Import CSS。在 Webpack 的世界,所有 Import 的檔案都稱為 Module,因此我們 Import 的 CSS、LESS 或是 SASS 檔案就概稱為 CSS Module。
我們先拋開 CSS Module 來看看在 React 中使用 CSS 有哪些做法,後面再來選擇 Boilerplate 應該實作何種方法。
直接將 Style 寫成 JS 物件,當作 Component 的 prop 傳遞進去:
class ExampleComponent extends Component {
render() {
return (
<div
style={{
border: '1px solid red',
}}
>
foo bar
</div>
);
}
}
將 CSS 獨立寫在外部檔案,再以 Module 形式載入,此方法必須搭配 Webpack 的設定。
/* styles.css */
.redBorder {
border: 1px solid red;
}
import cx from 'classnames';
import styles from './styles.css';
class ExampleComponent extends Component {
render() {
let { className } = this.props;
return (
<div className={cx(styles.redBorder, className)}>
foo bar
</div>
);
}
}
為了避免不同 Component 之間的 className 互相衝突,一般在 Webpack 的設定中會將 styles.css
中的 class 重新命名,例如加上一段 hash,或是加上 Scope 名稱。所以上例中的 redBorder
經過 Webpack 的 Loader 轉換後可能會被重新命名為 styles_redBorder_vgo
,其中 styles
是 Scope,vgo
則是一段 hash。
由於 className 是動態產生的,所以 assign 給 Component 時不能寫死成 className="redBorder"
,必須要以變數方式傳遞:className={styles.redBorder}
。
第三種作法和方法二類似,只是 Webpack 的設定少了一點,使用固定的 className,不做重新命名。
通常這個寫法是用在 Library(例如:react-dates),因為元件的 className 是寫死的,所以我們可以不採用 Library 本身提供的預設樣式自己刻一套,如此才能保留 Library 的彈性。
/* styles.css */
.redBorder {
border: 1px solid red;
}
import cx from 'classnames';
import './styles.css';
class ExampleComponent extends Component {
render() {
return (
<div className={cx('redBorder', className)}>
foo bar
</div>
);
}
}
這個方法是最近出現的,筆者也還沒使用過,有興趣的讀者可以看看 styled-components 這一套 Library。
我並不擅長前端,所以當初在規劃 Boilerplate 要如何整合 CSS 時並沒有做太多研究,而且開源界的風向也非常亂,前面提及的方法一至方法四都有人使用,當時就隨手挑了方法二 Dynamic ClassName 整合進 Boilerplate 中,而且還搭配了 sass
及 webpack-isomorphic-tools
一起使用。
其實越往後寫越發現方法三 Fixed ClassName 才是最佳解,所以在此跟各位讀者抱歉,我們的 Boilerplate 押錯了寶,選到了方法二,日後我應該會找個時間調整成方法三 QQ