運用 CSS in JS 實現模組化
運用 CSS in JS 實現模組化
一、什麼是 CSS in JS#
CSS in JS是2014年推出的一種設計模式,它的核心思想是把 CSS 直接寫到各自元件中,而不是單獨的樣式檔案裡。
CSS in js 的發展:
-
最早就是內聯樣式
-
依舊使用 CSS,但使用 JS 來管理樣式依賴,代表是 CSS Modules。
這種方式在React框架中引入的。
-
使用 JavaScript 生成 CSS 然後插入到頁面中的方式。例如 Styled Components。
CSS Module 還是 JS 和 CSS 分離的寫法,而 styled components 實際上是在 JS 上寫 CSS了。
CSS in js 一次又一次的違背了 CSS 與 JS 分離的原則。
二、常見的 CSS in JS#
1、CSS Modules#
CSS Modules 能最大化地結合現有 CSS 生態和 JS 模組化能力,API 簡潔到幾乎零學習成本。
(1)安裝#
CSS Modules 提供各種外掛,支援不同的構建工具。本文使用的是 Webpack 的css-loader
外掛。
CSS Modules不侷限於你使用哪個前端庫,無論是 React、Vue 還是 Angular,只要你能使用構建工具進行編譯打包就可以使用。
本文以 react 為例。下同。
(2)全域性/區域性作用域 #
CSS 是全域性的,沒有區域性作用域的功能。
但 CSS Modules 預設有區域性作用域的概念,實現的方法為:使用獨一無二的 class 名。
這個獨一無二的 class 名,是一個 hash 值,
css-loader
預設的生成演算法是[hash:base64]
,但 webpack 配置裡面可以自定義格式。CSS Modules 只會對
className
以及id
進行轉換,其他的比如屬性選擇器,標籤選擇器都不進行處理,推薦儘量使用 className。
寫法 - js:
Copyimport style from './App.css';
<h1 className={style.title_1}>
<h2 className={style.title_2}>
寫法 - css
Copy# 區域性作用域的兩種寫法
.title_1 {}
:local(.title_1) {}
# 全域性作用域的兩種寫法
:global(.title_2) {}
:global {
.title_2 {}
# 還能繼續新增……
}
生成 - html:
Copy<h1 class="_3zyde4l1yATCOkgn-DBWEL">
<h1 class="title_2">
生成 - css:
Copy._3zyde4l1yATCOkgn-DBWEL {}
.title_2{}
(3)拓展 - 實現區域性作用域的幾種做法#
1、巢狀(很深)選擇器
Copy.widget .table .row .cell .content .header .title {}
2、使用 BEM 的 class 命名規範
用很長的有規則的命名,來儘量實現唯一標識
CopyclassName="widget__header--active"
參考我之前的文章《運用 CSS methodologies 去實現模組化》有介紹 BEM。
3、css modules 的做法
直接用 hash 生成 class 名。即沒有方法1的巢狀,也絕對不會出現方法2中小概率的命名衝突問題。
(4)組合 composition#
composes
關鍵字可以讓一個選擇器可以繼承另一個選擇器的規則。
很像 less 裡的繼承。
用處:
1、可以引入別的模組,是實現模組化的一個必要功能。
2、還能引入別的模組的部分樣式。
寫法 - css:
Copy.title-base {
background-color: blue;
}
# 1、來源於本檔案
.title {
composes: title-base;
color: red;
}
# 2、或 來源於別的檔案
.title {
composes: title-base from './another.css';
color: green;
}
生成 - html:
Copy<h1 class="_2DHwuiHWMnKTOYG45T0x34 _10B-buq6_BEOTOl9urIjf8">
生成 - css:
Copy._10B-buq6_BEOTOl9urIjf8 {
background-color: blue;
}
._2DHwuiHWMnKTOYG45T0x34 {
color: red;
}
注意:這裡是繼承不是 mixin,所以這裡沒有混入所繼承的選擇器的屬性,而是直接 addon 選擇器名。減少了重複程式碼。
(5)使用變數#
方法1:PostCSS 和postcss-modules-values
方法2:搭配 less / sass
:export
關鍵字可以把 CSS 中的 變數輸出到 JS 中。
/* config.scss */
$primary-color: #f40;
:export {
primaryColor: $primary-color;
}
Copy
/* app.js */
import style from 'config.scss';
// 會輸出 #F40
console.log(style.primaryColor);
(6)結合#
1、跟 CSS 前處理器結合
2、跟 BEM 等 CSS methodologies 結合
可參考我之前的一篇文章:CSS methodologies 去實現模組化
(7)例項#
react - jsx :
Copyimport classNames from 'classnames';
…………
<div className={styles.header}>
<ul className={styles.menu}>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<div className={styles.account}>
<button
type="button"
className={classNames(styles.btnLogin, {
[styles.active]: !!this.state.active,
})}
>
login
</button>
<button type="button" className={styles.btnRegister}>
register
</button>
</div>
</div>
react - less :
Copy.header {
color: blue;
.menu {
color: red;
li {
color: green;
}
}
.account {
color: orange;
.btnLogin {
font-size: 15px;
}
.btnRegister {
font-size: 20px;
}
.btnLogin.active,
.btnRegister.active {
font-weight: bold;
}
}
}
注意點:
1、因為使用了 css module 所以不用擔心類名重複。可以捨棄掉 BEM 那種很長的類名,在保證基本語意化的前提下采取儘量簡單的類名。
2、建議類名為駝峰,因為 js 裡的 dot 取值形式對駝峰友好,而對styles.btn-login
會報錯。
3、可在 less 中採取Combined Class Names(如.btnRegister.active
)或Nested Class Names。
4、可以在 react 中用classname
庫提高書寫效率(很適合搭配 state / props )。
# classname 用法
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
5、寫好多的styles.xxx
很煩怎麼辦?可以用babel-plugin-react-css-modules自動加 styles 字首。例子:
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';
class Table extends React.Component {
render () {
return <div styleName='table'>
<div styleName='row'>
<div styleName='cell'>A0</div>
<div styleName='cell'>B0</div>
</div>
</div>;
}
}
export default CSSModules(Table, styles);
另外,還可以方便的覆蓋本地變數的樣式:
Copyimport customStyles from './table-custom-styles.css';<Table styles={customStyles} />;
6、更多實踐請參考 ant design pro (https://pro.ant.design/docs/style-cn),它們用的正是 css modules + less。
2、Styled Components#
(1)安裝#
npm install styled-components
(2)使用#
就拿一個 demo 舉例:
Copyimport styled from 'styled-components';
const Wrapper = styled.section`
margin: 0 auto;
width: 300px;
text-align: center;
`;
const Button = styled.button`
width: 100px;
color: white;
background: skyblue;
`;
render(
<Wrapper>
<Button>Hello World</Button>
</Wrapper>
);
注意:Styled Components 不支援 less 和 sass 語法。
由於 Styled Components 有些激進,本人目前不打算深入瞭解。
So,剩餘部分待寫。
拓展#
1、CSS 前處理器 和 CSS 後處理器#
共同點:CSS 前處理器 和 CSS 後處理器 都屬於 CSS 處理器。
不同點:CSS 前處理器使用特殊的語法來標記需要轉換的地方,而 CSS 後處理器可以解析轉換標準的 CSS,並不需要任何特殊的語法。
CSS 後處理器的代表就是PostCSS,PostCSS 是一個平臺,其中最常用到的外掛就是autoprefixer。
漫思