一、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/