1、
以前,網頁開發有一個原則,叫做“關注點分離”(separation of concerns)。
它的意思是,各種技術只負責自己的領域,不要混合在一起,形成耦合。對於網頁開發來說,主要是三種技術分離。
- HTML 語言:負責網頁的結構,又稱語義層
- CSS 語言:負責網頁的樣式,又稱視覺層
- JavaScript 語言:負責網頁的邏輯和互動,又稱邏輯層或互動層
簡單說,就是一句話,不要寫”行內樣式”(inline style)和”行內指令碼”(inline script)。比如,下面程式碼就很糟糕(檢視完整程式碼)。
1 2 3 |
<h1 style="color:red;font-size:46px;" onclick="alert('Hi')"> Hello World </h1> |
2、
React 出現以後,這個原則不再適用了。因為,React 是元件結構,強制要求把 HTML、CSS、JavaScript 寫在一起。
上面的例子使用 React 改寫如下(檢視完整程式碼)。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const style = { 'color': 'red', 'fontSize': '46px' }; const clickHandler = () => alert('hi'); ReactDOM.render( <h1 style={style} onclick={clickHandler}> Hello, world! </h1>, document.getElementById('example') ); |
上面程式碼在一個檔案裡面,封裝了結構、樣式和邏輯,完全違背了”關注點分離”的原則,很多人不適應。
但是,這有利於元件的隔離。每個元件包含了所有需要用到的程式碼,不依賴外部,元件之間沒有耦合,很方便複用。所以,隨著 React 的走紅和元件模式深入人心,這種”關注點混合”的新寫法逐漸成為主流。
3、
表面上,React 的寫法是 HTML、CSS、JavaScript 混合在一起。但是,實際上不是。現在其實是用 JavaScript 在寫 HTML 和 CSS。
React 在 JavaScript 裡面實現了對 HTML 和 CSS 的封裝,我們通過封裝去操作 HTML 和 CSS。也就是說,網頁的結構和樣式都通過 JavaScript 操作。
4、
React 對 HTML 的封裝是 JSX 語言 ,這個在各種 React 教程都有詳細介紹,本文不再涉及了,下面來看 React 對 CSS 的封裝。
React 對 CSS 封裝非常簡單,就是沿用了 DOM 的 style 屬性物件,這個在前面已經看到過了。
1 2 3 4 |
const style = { 'color': 'red', 'fontSize': '46px' }; |
上面程式碼中,CSS 的font-size
屬性要寫成fontSize
,這是 JavaScript 操作 CSS 屬性的約定。
由於 CSS 的封裝非常弱,導致了一系列的第三方庫,用來加強 React 的 CSS 操作。它們統稱為 CSS in JS,意思就是使用 JS 語言寫 CSS。根據不完全統計,各種 CSS in JS 的庫至少有47種。老實說,現在也看不出來,哪一個庫會變成主流。
你可能會問,它們與”CSS 前處理器”(比如 Less 和 Sass,包括 PostCSS)有什麼區別?回答是 CSS in JS 使用 JavaScript 的語法,是 JavaScript 指令碼的一部分,不用從頭學習一套專用的 API,也不會多一道編譯步驟。
5、
上週,我看到一個新的 CSS in JS 庫,叫做 polished.js。它將一些常用的 CSS 屬性封裝成函式,用起來非常方便,充分體現使用 JavaScript 語言寫 CSS 的優勢。
我覺得這個庫很值得推薦,這篇文章的主要目的,就是想從這個庫來看怎麼使用 CSS in JS。
首先,載入 polished.js。
1 |
const polished = require('polished'); |
如果是瀏覽器,插入下面的指令碼。
1 2 |
<script src="https://unpkg.com/polished@1.0.0/dist/polished.min.js"> </script> |
polished.js
目前有50多個方法,比如clearfix
方法用來清理浮動。
1 2 3 |
const styles = { ...polished.clearFix(), }; |
上面程式碼中,clearFix
就是一個普通的 JavaScript 函式,返回一個物件。
1 2 3 4 5 6 7 8 |
polished.clearFix() // { // &::after: { // clear: "both", // content: "", // display: "table" // } // } |
“展開運算子”(...
)將clearFix
返回的物件展開,便於與其他 CSS 屬性混合。然後,將樣式物件賦給 React 元件的style
屬性,這個元件就能清理浮動了。
1 2 3 4 |
ReactDOM.render( <h1 style={style}>Hello, React!</h1>, document.getElementById('example') ); |
從這個例子,大家應該能夠體會polished.js
的用法。
6、
下面再看幾個很有用的函式。
ellipsis
將超過指定長度的文字,使用省略號替代(檢視完整程式碼)。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const styles = { ...polished.ellipsis('200px') } // 返回值 // { // 'display': 'inline-block', // 'max-width': '250px', // 'overflow': 'hidden', // 'text-overflow': 'ellipsis', // 'white-space': 'nowrap', // 'word-wrap': 'normal' // } |
hideText
用於隱藏文字,顯示圖片。
1 2 3 4 5 6 7 8 9 10 11 12 |
const styles = { 'background-image': 'url(logo.png)', ...polished.hideText(), }; // 返回值 // { 'background-image': 'url(logo.png)', 'text-indent': '101%', 'overflow': 'hidden', 'white-space': 'nowrap', } |
hiDPI
指定高分屏樣式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const styles = { [polished.hiDPI(1.5)]: { width: '200px', } }; // 返回值 //'@media only screen and (-webkit-min-device-pixel-ratio: 1.5), // only screen and (min--moz-device-pixel-ratio: 1.5), // only screen and (-o-min-device-pixel-ratio: 1.5/1), // only screen and (min-resolution: 144dpi), // only screen and (min-resolution: 1.5dppx)': { // 'width': '200px', //} |
retinaImage
為高分屏和低分屏設定不同的背景圖。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const styles = { ...polished.retinaImage('my-img') }; // 返回值 // backgroundImage: 'url(my-img.png)', // '@media only screen and (-webkit-min-device-pixel-ratio: 1.3), // only screen and (min--moz-device-pixel-ratio: 1.3), // only screen and (-o-min-device-pixel-ratio: 1.3/1), // only screen and (min-resolution: 144dpi), // only screen and (min-resolution: 1.5dppx)': { // backgroundImage: 'url(my-img_2x.png)', // } |
7、
polished.js
提供的其他方法如下,詳細用法請參考文件。
normalize()
:樣式表初始化placeholder()
:對 placeholder 偽元素設定樣式selection()
:對 selection 偽元素設定樣式darken()
:調節顏色深淺lighten()
:調節顏色深淺desaturate()
:降低顏色的飽和度saturate()
:增加顏色的飽和度opacify()
:調節透明度complement()
:返回互補色grayscale()
:將一個顏色轉為灰度rgb()
:指定紅、綠、藍三個值,返回一個顏色rgba()
:指定紅、綠、藍和透明度四個值,返回一個顏色hsl()
:指定色調、飽和度和亮度三個值,返回一個顏色hsla()
:指定色調、飽和度、亮度和透明度三個值,返回一個顏色mix()
:混合兩種顏色em()
:將畫素轉為 emrem()
:將畫素轉為 rem
目前,polished.js
只是1.0版,以後應該會有越來越多的方法。
8、
polished.js
還有一個特色:所有函式預設都是柯里化的,因此可以進行函式組合運算,定製出自己想要的函式。
1 2 3 4 |
import { compose } from 'ramda'; import { lighten, desaturate } from 'polished'; const tone = compose(lighten(10), desaturate(10)) |
上面程式碼使用 Ramda 函式庫完成組合運算。Ramda 的用法可以參考我寫的教程。