CSS也能像組件狀態(tài)一樣響應式更新?
大家好,我是卡頌。
不知道平時在項目里你怎么處理CSS呢?
我們知道,由于原生CSS存在一些問題,比如:
- 復用時容易樣式?jīng)_突
- 沒有作用域、沒有模塊化
- 沒有編程能力
社區(qū)涌現(xiàn)出很多解決方案,比如:
- 命名規(guī)范(比如BEM規(guī)范)
- 模塊規(guī)范(CSS Modules)
- CSS預處理器(比如Less)
- CSS In JS
- CSS框架(Tailwind CSS)
- ......
如果我們按以下三個維度評判這些方案:
- 上手難度:與原生CSS越接近,越好上手
- 靈活性:擁有越強編程能力,越靈活
- 能力:能解決多少原生CSS的問題
會發(fā)現(xiàn)每個方案都有自己的優(yōu)勢與短板。

比如:
- CSS In JS方案用JS寫CSS,擁有極高靈活性,但加大了上手難度
- Less(CSS預處理器)可以看作CSS的超集,上手難度低、有一定編程能力,但是CSS自身的問題他也存在
業(yè)界常見做法是:同時使用BEM規(guī)范(解決命名沖突問題)+ CSS預處理器。
進擊的Vue CSS解決方案
我們用這三個維度分析下Vue的SFC(Single-File-Component,單文件組件):
- <template>
- <p>xxx</p>
- </template>
- <script>
- // ...
- </script>
- <style scoped>
- p {
- color: #0f0;
- }
- </style>
- 上手難度:樣式在<style>標簽內(nèi)書寫,與原生CSS別無二致,上手簡單,符合直覺
- 能力:scoped標識提供了「模塊化」能力
- 靈活性:可以使用各種預處理器,有一定靈活性
可以看到,Vue SFC采用的是一種各方面沒有明顯短板,局部很突出(上手難度低)的CSS方案。
隨著Vue3.2發(fā)布,Vue SFC中的CSS屬性獲得了「響應式更新能力」,使其靈活性大大提升。
響應式CSS屬性
對于如下Vue SFC:
- <template>
- <div class="text">hello</div>
- </template>
- <script>
- export default {
- data() {
- return {
- color: 'red'
- }
- }
- }
- </script>
- <style>
- .text {
- color: v-bind(color);
- }
- </style>
<script>標簽內(nèi)定義了狀態(tài)color = 'red'
.text使用v-bind為color屬性綁定該狀態(tài)。效果如下:
為了驗證響應式更新能力, 為div增加點擊事件:
- <div class="text" @click="color= color === 'red' ? 'green' : 'red'">hello</div>
點擊后會讓color狀態(tài)在red與green間切換??梢钥吹?,頁面樣式也會同步變化:
Demo地址[1]
不僅是color,你可以為任何CSS屬性綁定狀態(tài)。
那么這個特性是如何實現(xiàn)的呢?
實現(xiàn)原理每個使用v-bind綁定到CSS屬性的狀態(tài)對應一個CSS變量,該CSS變量會作為style屬性賦值給組件最外層DOM。
在我們的例子中:
- .text {
- color: v-bind(color);
- }
其中v-bind(color)會成為CSS變量:
并作為style屬性賦值給div:
.text經(jīng)過編譯會使用該CSS變量:
- .text {
- // 編譯前
- /* color: v-bind(color); */
- // 編譯后
- color: var(--469af010-color);
- }
當顏色變化后,CSS變量的值隨之變化:
所以,要使用這個特性需要目標瀏覽器支持CSS變量。
Vue3放棄IE這可是說到做到的。
總結
Vue官方稱該特性為State-Driven Dynamic CSS[2]。
經(jīng)過這波操作,Vue SFC的CSS靈活性有了很大提高。
并且,有了v-bind這個開頭,相信未來會出現(xiàn)更多與「響應式更新」掛鉤的「自定義CSS指令」。
之前的自定義指令都是運行時的,以后的指令可能會是基于AST的編譯時了。這種轉(zhuǎn)變,你接受嗎?
參考資料
[1]Demo地址:
https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHRlbXBsYXRlPlxuICA8ZGl2IGNsYXNzPVwidGV4dFwiIEBjbGljaz1cImNvbG9yPSBjb2xvciA9PT0gJ3JlZCcgPyAnZ3JlZW4nIDogJ3JlZCdcIj5oZWxsbzwvZGl2PlxuPC90ZW1wbGF0ZT5cblxuPHNjcmlwdD5cbmV4cG9ydCBkZWZhdWx0IHtcbiAgZGF0YSgpIHtcbiAgICByZXR1cm4ge1xuICAgICAgY29sb3I6ICdyZWQnXG4gICAgfVxuICB9XG59XG48L3NjcmlwdD5cblxuPHN0eWxlPlxuLnRleHQge1xuICBjb2xvcjogdi1iaW5kKGNvbG9yKTtcbn1cbjwvc3R5bGU+In0=
[2]State-Driven Dynamic CSS:
https://v3.vuejs.org/api/sfc-style.html#state-driven-dynamic-css