探索原因背景
首先自然是項目有需求,這是必須去做的原因
其次鸥拧,是我們項目沒有直接使用市面上現(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
這里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的變量,
如上圖硬纤,在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文件中
這是無法接受的
第一是項目樣式考慮兼容性等原因,需經(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)邏輯和原理》 來研究下