我們平時在使用Elementui Antdesing這些UI庫時,難免會碰到使用deep強行侵入式去修改元件內部樣式的情況;
比如下列程式碼,我們需要把ant的分頁樣式進行高度自定義,就得使用deep去修改;
這種實現方式確實能夠達到我們的目的,但在開發時確總覺得不太合適:
1、他屬於強行入侵元件內部去修改,雖然不會有大問題,但總覺得這種取巧方式有點不可取。
2、每次寫deep時需要手動檢查DOM元素進行編輯,UI文件中並不會有相關API文件;
為了解決心中的這兩個結症,趁最近寫元件的機會探索了下CSS 的var函式,發現他可以很好的解決以上問題。
利用var函式我們可以具體解決元件開發的以下問題:
1、元件樣式自定義可以更加個性化
2、現在很多元件庫實現一些樣式控制還是依靠props傳參的形式,這種var函式結合Style可完全避免props傳參的情況,把樣式跟props區分開;
3、在元件文件中可明確定製樣式屬性API;使元件樣式可以跟Props屬性一樣以傳參概念去進行高度定製,在使用元件樣式時不需要像使用deep一樣開啟f12去一層一層的找
4、不會侵入式的改動元件內部樣式
5、這個方案與deep互不排斥,如果開放的樣式API不夠用還是可以繼續使用deep;
看下面需求,是如何利用css var函式實現一個可自定義大小,顏色,以及子元素樣式的卡片
上程式碼
//Card.vue <template> <div class="card"> <img class="card-img" :src="img" /> <p class="card-desc">{{ desc }}</p> </div> </template> <style lang="scss" scoped> .card { // 這些以"--"開頭的變數就是我們開放可自定義的樣式屬性了,可以在元件使用文件中明確開放 //卡片根元素樣式 --width: 150px; --height: auto; --border-size: 1px; --border-color: #ccc; --border-radius: 5px; --bg: #eee; // 圖片可定樣式 --img-width: 130px; --img-height: 130px; --img-radius: 50%; // 卡片描述樣式 --desc-size: 16px; --desc-line-height: 32px; --desc-color: #333; height: var(--height); width: var(--width); border: var(--border-size) solid var(--border-color); border-radius: var(--border-radius); background: var(--bg); padding: 10px; &-img { width: var(--img-width); height: var(--img-height); border-radius: var(--img-radius); overflow: hidden; } &-desc { font-size: var(--desc-size); color: var(--desc-color); line-height: var(--desc-line-height); text-align: center; } } </style>
這時候我們在API文件則可明確規定樣式定製屬性了:
元件使用方式:
//demo.vue <template> <div > <Card desc="我是預設的樣式我是預設的樣式我是預設的樣式" :img="img" /> <Card class="card_1" desc="自定義樣式,子元素圖片變小了" :img="img" /> <Card class="card_2" desc="自定義樣式,圓角沒了,描述字變小了,高度高了" :img="img" /> </div> </template> <script>...</script> <style lang="scss" scoped> .card_1 { --width: 100px; --height: 200px; --border-radius: 20px; --img-width: 80px; --img-height: 50px; --img-radius: 10px; --desc-color: #f00; --desc-size: 12px; --desc-line-height: 21px; } .card_2 { --height: 300px; --border-radius: 0px; --bg: #fff; --img-radius: 50px; --desc-size: 14px; --desc-line-height: 21px; } </style>
以上就是一個基本的元件實現;
還種情況我們在使用UI庫時也會碰到需要使用Props傳過來計算的引數,比如下列程式碼:
這種我們也可以把這些全提出來使用style屬性。讓樣式與js引數徹底隔離
//demo.vue <template> <card desc="我是預設的樣式我是預設的樣式" :img="img" :style="hoverStyle" @mouseout="hoverStyle = {}" @mouseover="handleHover" /> </template> <script setup> let hoverStyle = ref({}); const handleHover = () => { hoverStyle.value = { '--bg': '#f0f', '--width': '180px' }; }; </script>
我們在元件內
JS可以使用props.style獲取到設定的值,
css中可以使用calc變數與var結合去計算你想要的值
//card.vue <template> <div class="card" :style="style"> {{ width }} <img class="card-img" :src="img" /> <p class="card-desc">{{ desc }}</p> </div> </template> <script setup> const $props = defineProps({ img: { type: String, default: '', }, desc: { type: String, default: '', }, style: { type: Object, default: () => ({}), }, }); //假如你在js中需要用到寬度 let width = computed(() => { return parseInt($props.style['--width'] || 150); }); </script> <style lang="scss" scoped> .card{ ... //假如你有個子級元素需要基於寬度計算 .item{ width: calc(var(--width) - 100) } ... } </style>
但是這種實現有名稱空間的問題
所以需要稍微注意下變數命名,最好加獨有的命名規則。防止變數覆蓋;
//比如這樣 .ch-card{ --ch-card-width:100px; --ch-card-height:100px; }
總結下這種方案的優缺點:
優點
1、元件樣式自定義可以更加個性化
2、現在很多元件庫實現一些樣式控制還是依靠props傳參的形式,這種var函式結合Style可完全避免props傳參的情況,把樣式跟props區分開;
3、在元件文件中可明確定製樣式屬性API;使元件樣式可以跟Props屬性一樣以傳參概念去進行高度定製,在使用元件樣式時不需要像使用deep一樣開啟f12去一層一層的找
4、不會侵入式的改動元件內部樣式
5、這個方案與deep互不排斥,如果開放的樣式API不夠用還是可以繼續使用deep;
缺點:
1、開發元件時會工作量會增大,但是磨刀不誤砍柴功
2、名稱空間問題導致命名會有點長。
歡迎分享更好的解決方案。