淺析前端一鍵換膚5種方案:css樣式覆蓋、實現多套css主題、css自定義變數實現、webpack-theme-color-replacer外掛實現自定義主題色、UI框架自定義主題功能
一、css 樣式覆蓋實現
1、核心:通過切換 CSS 選擇器的方式實現主題樣式的切換
- 在元件中保留不變的樣式,將需要變化的樣式進行抽離
- 提供多種樣式,給不同的主題定義一個對應的 CSS 選擇器
- 根據不同主題設定不同的樣式
2、如何實現:
(1)通過 vuex
儲存和控制全域性的主題色;
(2)在 template
模板中通過 vuex
中的主題設定對應類名
(3)比如 theme.css
中通過 .light
和 .dark
兩個類選擇器來區分明亮主題和暗黑主題,並且事先準備了它們對應的樣式
這種方式比較簡單,就不多說。
3、缺點:
多種主題樣式都要引入,程式碼量大;樣式不易管理,需要改的話,幾個主題樣式裡都需要改;樣式不易查詢,導致開發效率低等
二、實現多套 CSS 主題樣式
1、核心:實現多套 CSS 主題樣式,根據使用者切換操作,通過 link
標籤動態載入不同的主題樣式,主要解決了多個主題色被編譯到一個檔案中導致單個檔案過大的問題
PS:在很多給程式碼設定樣式的時候,都可以見到這種不同主題樣式的情景
2、實現:
(1)css 部分直接拆分成 ligth.css
和 dark.css
兩個檔案
(2)設定主題部分的 setTheme.js
程式碼如下
export default function setTheme(theme = 'ligth') {
let link = document.querySelector(' #theme-link')
let href = "/theme/" + theme + ".css"
if (!link) {
let head = document.querySelector('head')
link = document.createElement('link')
link.id = '#theme-link'
link.rel = "stylesheet"
link.href = href
head.appendChild(link)
} else {
link.href = href
}
}
3、缺點:其實跟第一種差不多,與第一種方案相比,這種解決了多個主題色被編譯到一個檔案,導致檔案過大問題。但是這種方式又沒法把“變”與“不變”的樣式分開,比如有些不需要主題色的樣式,其實是“不變的”,應該不需要在每個 css 檔案中都寫一套的。
4、這樣其實演化出進化版本:
(1)不變的 css 作為一個檔案,如:common.css
(2)根據主題色變化的 css 作為多個主題色檔案,如:light.css 和 dark.css
三、css 變數實現
// 無UI庫依賴的主題切換
// 核心思想:css3 中的 :root 偽類選擇器和 var 變數的應用
// 1、定義主題變數
:root {
--theme-color: #ccc;
}
// 2、使用主題變數
.test{
color: var(--theme-color);
}
// 3、動態改變主題
document.documentElement.style.setProperty('--theme-color', '#fff');
1、核心:通過 body.style.setProperty(key, value)
動態修改 body 上的 CSS 變數,使得頁面上的其他部分可以應用最新的 CSS 變數對應的樣式。
2、實現:
(1)比如 theme.css 中負責定義全域性的 CSS 變數
/* 實現方式一 */
:root {
--theme-bg: initial; // 背景色
--theme-color: initial; // 字型色
--theme-boder-color: initial; // 邊框色
}
/* 實現方式二 */
/* 預設值:light */
:root {
--theme-bg: #fff;
--theme-color: rgb(51, 50, 50);
--theme-img-bg: #fff;
--theme-boder-color: #d6d6d6;
}
/* 暗黑:dark */
[data-theme='dark'] {
--theme-bg: rgb(51, 50, 50);
--theme-color: #fff;
--theme-boder-color: #fff;
}
這裡的實現方式一,就是通過 setProperty() 方法用於設定一個新的 CSS 屬性,同時也可以修改 CSS 宣告塊中已存在的屬性。
實現方式二,就是通過區域性css變數覆蓋全域性css變數的原理
(2)比如 themeUtil.js 中負責獲取當前對應樣式值,以及設定 body 上的 CSS 變數值,如
const darkTheme = 'rgb(51, 50, 50)'
const lightTheme = '#fff'
const lightBorderTheme = '#d6d6d6'
// 獲取對應的主題色值
export const getThemeMap = (isLight) => {
return { // 這裡其實可以通過 key value 去設定多種主題的樣式
'theme-bg': isLight ? lightTheme : darkTheme,
'theme-color': isLight ? darkTheme : lightTheme,
'theme-boder-color': isLight ? lightBorderTheme : lightTheme,
}
}
// 設定主題色值
export const setTheme = (isLight = true) => {
const themeMap = getThemeMap(isLight)
const body = document.body
/* 實現方式一 */ 設定全域性的css變數
Object.keys(themeMap).forEach(key => {
body.style.setProperty(`--${key}`, themeMap[key])
})
/* 實現方式二 */ 設定區域性的css變數,區域性的會覆蓋全域性的變數
// body.setAttribute('data-theme', isLight ? 'light' : 'dark')
}
(3)通過 var()
在元件中應用對應 CSS 變數,比如在頭部中的使用:color: var(--theme-color);
3、缺點:相容性不好
4、優化:可通過 css-vars-ponyfill
對 CSS 變數進行相容處理
四、webpack-theme-color-replacer外掛實現自定義主題色
以上方案都是我們需要事先知道有哪些主題色方案,但是,如果主題不固定的,怎麼辦呢?
也有方案實現:可借用webpack外掛:webpack-theme-color-replacer 來實現,但我沒具體用過,想了解的搜一下吧,網上挺多介紹如何做的。
五、UI 框架自定義主題功能
比如 ElementUI 的自定義主題,具體見官方文件:https://element.eleme.cn/#/zh-CN/component/custom-theme
Ant-design-vue 定製主題,具體見官方文件:https://www.antdv.com/docs/vue/customize-theme-cn/