前端組件庫自定義主題切換探索-01-方案借鑒與思路參考

探索原因背景

首先自然是項目有需求,這是必須去做的原因
其次鸥拧,是我們項目沒有直接使用市面上現(xiàn)成的基于element-ui或者ant-design的第三方UI框架党远,比如avue,而是有著自己的UI組件庫
第三富弦,我們的組件庫基于ant-design-vue沟娱,而ant-design-vue并沒有很好的支持主題動態(tài)切換(主題總體模式僅支持深色和淺色,其他顏色只支持主色切換腕柜,不支持其他顏色和屬性動態(tài)定制)
第四济似,我們還在使用Vue2,這也許是最痛苦的矫废,因為vue3的UI庫比如ant-design-vue,直接支持動態(tài)主題

期望目標和效果

1碱屁、用戶可以自定義主題色
2磷脯、用戶可以切換內置好的主題
3、用戶可以同時自定義配置主題其他顏色細節(jié)(包括除主題以外的其他顏色娩脾,比如警告色赵誓,錯誤顏色等)
4、用戶可以同時自定義非顏色的細節(jié)柿赊,比如輸入框圓角俩功,placeholder顏色,組件尺寸等
5碰声、不同的組件可以單獨定制
6诡蜓、同時支持ant-design-vue和自研組件庫的主題定制

方案和思路探索

在開始之前,我們先研究下現(xiàn)有的方案胰挑,參照下別人的思路

可用方案參考

1蔓罚、樣式覆蓋
通過切換 css 選擇器的方式實現(xiàn)主題樣式的切換
2、多套css切換
即寫多套css瞻颂,覆蓋原有的豺谈,并且根據(jù)條件切換css
比如 bladex 的主題皮膚切換就是這樣做的,不同的皮膚主題贡这,對應不同的css

image.png

image.png

這里iview主題茬末,對應view.scss,d2主題,對應d2.scss

3盖矫、css變量實現(xiàn)

  • 變量聲明
    :root {
     --primary-color: #878ef6;
     --warning-color: #98efd7;
    }
    div{
       --primary-color: #878ef6;
     --warning-color: #98efd7;
    }
    

其中 :root表示全局變量丽惭,而使用其他的比如在div里面聲明的,只對div標簽有效

  • 變量的使用
    使用時用var()函數(shù)獲取
.button-primary {
 color: var(--primary-color);
}
.button-warning {
 color: var(--warning-color, "#e98321");
}

其中辈双,var函數(shù)的第二個參數(shù)表示默認值

  • 使用javascript操作
// 設置變量
document.body.style.setProperty('--primary-color', '#878ef6');

// 讀取變量
document.body.style.getPropertyValue('--primary-color').trim();
// '#7F583F'

// 刪除變量
document.body.style.removeProperty('--primary-color');

  • 兼容性處理
    css變量在低端瀏覽器不被支持责掏,需要使用 css-vars-ponyfill 做兼容處理

方案優(yōu)缺點

以上方案和方案優(yōu)缺點參考文章:前端 “一鍵換膚“ 的幾種方案

總體來說,使用變量覆蓋和多套css切換湃望,都不能很自由的滿足主題樣式定制拷橘,css變量可以動態(tài)的進行樣式設置,是最優(yōu)方案
a喜爷、要切換的目標主題得是我們首先定制好的冗疮,比如黑色,白色主題檩帐。
b术幔、用戶無法自己設置自己的主題,比如自己制定一個藍色主題
c湃密、css變量能滿足這些需求诅挑,理論上我們可以讓用戶自己設置他喜歡的任意部位的顏色樣式

我們面臨的其他問題

如果我們的UI組件庫完全是自己基于vue原生開發(fā)的四敞,那選用css變量方案,無疑是最佳方案拔妥。
然而忿危,我們的組件庫是基于ant-design-vue 1.x 封裝的,由于一些原因目前還不能使用vue3没龙。這里強調1.x铺厨,是因為基于vue3 的 ant-design-vue 3.x已經(jīng)提供了css變量來實現(xiàn)動態(tài)主題定制

ant-design-vue 1.x 的主題變量使用less的變量,


image.png

如上圖硬纤,在ant-design-vue 的 style/theme里面定義了default.less解滓,如果我們只是想靜態(tài)的改變主題,那重寫或者覆蓋這些變量即可

那能不能動態(tài)的改變less的變量顏色筝家,達到類似css變量的效果呢洼裤?有,我們來看看別人的參考方案

  • 在線編譯less變量方案
    該方案參考文章:動態(tài)改變主題顏色溪王,less變量動態(tài)改變
    該方案的實現(xiàn)腮鞍,參考上述文章,經(jīng)實踐確實可以達到動態(tài)改變Less變量的目的莹菱,然而對我們確無法適用缕减。該方案缺點:
    a、由于是動態(tài)編譯less芒珠,因此我們需要類似如下的結構。在public中新建一個color.less文件搅裙,在文件中定義好變量皱卓,并且將對應的樣式寫在該less文件中
    image.png

這是無法接受的
第一是項目樣式考慮兼容性等原因,需經(jīng)編譯器處理后部逮,統(tǒng)一輸出成css
第二是如果一個龐大的項目使用了這種在線編譯Less的結構娜汁,在性能上估計會有不少耗損(暫時沒有去實踐和驗證大項目的在線編譯)
第三我們使用的有自己的組件庫以及組件庫的基礎UI庫 ant-design-vue,組件庫使用npm管理兄朋,且有基礎組件庫和業(yè)務組件庫之分掐禁,這種結構不利于組件庫的使用,也不利于樣式的管理(動態(tài)編譯時需要去查找color.less的路徑)

b颅和、無法覆蓋ant-design-vue的主題樣式傅事。原因是less作為css預編譯語言使用,在ant-design-vue里面只存在于開發(fā)模式下峡扩,生產(chǎn)環(huán)境打包后的只有css蹭越,類似@primary-color這樣的變量也不會存在,已經(jīng)被轉換為 #D04A02這樣的具體顏色

  • 使用webpack-theme-color-replacer webpack插件動態(tài)修改顏色
    首先ant-design-vue-pro UI框架就是使用了這個插件來動態(tài)修改顏色教届,至于其他參考文章响鹃,可能你看到的大部分都是關于element-ui的實現(xiàn)驾霜,因為這個插件里面直接包含了一個forElementUI工具函數(shù)庫

webpack-theme-color-replacer webpack 基本思路就是,webpack構建時买置,在emit事件(準備寫入dist結果文件時)中粪糙,將即將生成的所有css文件的內容中 帶有指定顏色的css規(guī)則單獨提取出來,再合并為一個theme-colors.css輸出文件忿项。然后在切換主題色時蓉冈,下載這個文件,并替換為需要的顏色倦卖,應用到頁面上

所以我們先直接使用ant-design-vue-pro的修改主題代碼來測試一下
首先我們在項目根目錄建文件夾config,在文件夾下建文件plugin.config.js,用來注冊插件
plugin.config.js

const ThemeColorReplacer = require("webpack-theme-color-replacer")
const generate = require("@ant-design/colors/lib/generate").default

const getAntdSerials = (color) => {
  // 淡化(即less的tint)
  const lightens = new Array(9).fill().map((t, i) => {
    return ThemeColorReplacer.varyColor.lighten(color, i / 10)
  })
  console.log("lightens", lightens)
  const colorPalettes = generate(color)
  console.log("colorPalettes", colorPalettes)
  const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace("#", "")).join(",")
  // console.log("rgb", rgb)
  const matchColors = lightens.concat(colorPalettes).concat(rgb)
  // console.log("matchColors", matchColors)
  return matchColors
}

const themePluginOption = {
  matchColors: [
      ...getAntdSerials("#1890ff"), // 主色系列 1890ff
  ], // 主色系列 1890ff
}

const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(themePluginOption)

module.exports = createThemeColorReplacerPlugin

這里特別要注意的是getAntdSerials("#1890ff")洒擦,getAntdSerials 函數(shù)傳遞的顏色參數(shù),一定要是ant-design-vue的色系顏色怕膛,否則webpack-theme-color-replacer webpack 無法提取出正確的css字符熟嫩。這里傳遞的#1890ff 正是ant-design-vue的默認主題藍色

然后在vue.config.js中注冊插件
vue.config.js

const createThemeColorReplacerPlugin = require("./config/plugin.config")
module.exports = {
  configureWebpack: config => {
    config.plugins.push(createThemeColorReplacerPlugin())
  },
  css: {
    loaderOptions: {
      less: {
        javascriptEnabled: true
      }
    }
  },
}

新建測試頁面theme-example,寫幾行測試效果的代碼

<template>
      <div>
        <a-button type="primary">主色</a-button>
        <a-button type="danger">警告色</a-button>
        <a-button @click="changeTheme">點擊切換</a-button>
      </div>
</template>
<script lang="ts">
import { Component, Vue, Ref } from "vue-property-decorator"
import { updateTheme } from "@ant-design-vue/pro-layout"

@Component()
export default class ThemeExample extends Vue {
  isDefault= true // 是否默認
  /**
   * 改變主題色
   */
  changeTheme() {
    this.isDefault= !this.isDefault
    updateTheme(this.isDefault?  "#1890ff" : "#cf56d7")
  }
}
</script>

然后隨便找個路由頁面引入上述組件或者直接把他做成路由組件測試效果吧

[video(video-z7hFSmxg-1673186996605)(type-csdn)(url-https://live.csdn.net/v/embed/268750)(image-https://video-community.csdnimg.cn/vod-84deb4/0968a0708f5471edbfdf0764a0ec0102/snapshots/58c240f0130f4e5aa42669f280d26105-00001.jpg?auth_key=4826782667-0-0-a0b017aeb824a925545af9b494d65564)(title-20230108_205657)]

確實可以成功切換,但是只能切換主題色褐捻,如果我們想切換其他顏色掸茅,比如危險顏色,怎么做呢柠逞?
可以將 getAntdSerials 函數(shù)的顏色參數(shù)換成 ant-design-vue的危險色 #F5222D昧狮,即 getAntdSerials("#F5222D"),然后我們重啟下項目板壮,記得要重啟逗鸣,因為vue.config.js要重新注冊插件,重啟后看效果

[video(video-01qtjoiC-1673186985298)(type-csdn)(url-https://live.csdn.net/v/embed/268751)(image-https://video-community.csdnimg.cn/vod-84deb4/42e4be408f5671edbfe26723b78e0102/snapshots/1f6a1bdd772b4982beaff5237fd4f741-00001.jpg?auth_key=4826783623-0-0-2c7d84dac6ca370e08da569abbf25e47)(title-20230108_210855)]

可以看到這次切換的顏色是危險色绰精,即a-button type="danger" 時的顏色撒璧,而默認的主題色type="primary"并沒有被切換

那該方案是否能滿足我們的需求和期望呢?
答案是否定的笨使,對比下文章開頭卿樱,我們的需求和期望,只有第一點可以滿足硫椰,就是自定義主題色繁调,危險色雖然也能定制,但是我們卻無法同時分別定制主題色和危險色靶草。

那我們不妨來研究下 webpack-theme-color-replacer webpack 的實現(xiàn)原理蹄胰,看看是否可以改進后達到我們的目標和需求。內容比較長奕翔,我們在下一篇文章 《前端組件庫自定義主題切換探索-02--webpack-theme-color-replacer webpack 的實現(xiàn)邏輯和原理》 來研究下

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末烤送,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子糠悯,更是在濱河造成了極大的恐慌帮坚,老刑警劉巖妻往,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異试和,居然都是意外死亡讯泣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門阅悍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來好渠,“玉大人,你說我怎么就攤上這事节视∪” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵寻行,是天一觀的道長霍掺。 經(jīng)常有香客問我,道長拌蜘,這世上最難降的妖魔是什么杆烁? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮简卧,結果婚禮上兔魂,老公的妹妹穿的比我還像新娘。我一直安慰自己举娩,他們只是感情好析校,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铜涉,像睡著了一般智玻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上骄噪,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音蠢箩,去河邊找鬼链蕊。 笑死,一個胖子當著我的面吹牛谬泌,可吹牛的內容都是我干的滔韵。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼掌实,長吁一口氣:“原來是場噩夢啊……” “哼陪蜻!你這毒婦竟也來了?” 一聲冷哼從身側響起贱鼻,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤宴卖,失蹤者是張志新(化名)和其女友劉穎滋将,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體症昏,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡随闽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肝谭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掘宪。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖攘烛,靈堂內的尸體忽然破棺而出魏滚,到底是詐尸還是另有隱情,我是刑警寧澤坟漱,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布鼠次,位于F島的核電站,受9級特大地震影響靖秩,放射性物質發(fā)生泄漏须眷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一沟突、第九天 我趴在偏房一處隱蔽的房頂上張望花颗。 院中可真熱鬧,春花似錦惠拭、人聲如沸扩劝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棒呛。三九已至,卻和暖如春域携,著一層夾襖步出監(jiān)牢的瞬間簇秒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工秀鞭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趋观,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓锋边,卻偏偏與公主長得像皱坛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子豆巨,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容