最近項目涉及換主題/換膚的工作, 查了查資料,總結出五種換膚方案:
序號 | 方法 | 特點 |
---|---|---|
1 | 利用class 命名空間 | 最簡單的換膚方案 |
2 | 準備多套CSS主題 | 傳統前端最常用 |
3 | 利用CSS預處理生成多套主題樣式 | 現代前端最常用 |
4 | 動態換膚 | 支持瀏覽器熱換膚,最酷炫 |
5 | CSS變量換膚 | 不考慮IE,最佳換膚方式 |
這是五種均為通用方案,可以適用于各種前端框架,腳手架中
1. 利用class 命名空間
這是最簡單的換膚方式, 看下面示例即可輕松理解。
1.利用class 名稱準備兩個主題:
<style>
p.red-theme {
color: red
}
p.blue-theme {
color: blue
}
</style>
2.如果用紅色主題, 給body增加 red-theme標簽
<body class="red-theme">
<p> 這里是紅色主題 </p>
...
</body>
3.如果用藍色主題, 用 blue-theme 代替 red-theme
<body class="blue-theme">
<p> 這里是藍色主題 </p>
...
</body>
優缺點
- 優點: 簡單,好理解,好實現
- 缺點: CSS中需多寫主題的class,代碼容易混亂;需手動編寫
參考
2.準備多套CSS主題
本地存放多套主題, 根據需要切換加載不同的樣式
- 準備份默認樣式主題
/*theme-default.css*/
p {
color: #333
}
...
- 準備各主題的樣式
/* theme-red.css */
p {
color: #red
}
...
/* theme-blue.css */
p {
color: #blue
}
...
- 頁面加載后,根據用戶需求加載不同的樣式列表
var link = document.createElement('link');
link.type = 'text/css';
link.id = "theme-blue";
link.rel = 'stylesheet';
link.href = '/css/theme-blue.css';
document.getElementsByTagName("head")[0].appendChild(link);
- 有時候需要保存用戶使用的主題,可以通過如下方式:
- 利用路由標記
- 利用cookie標記
- 利用localstorage
- 保存到后端服務器
優缺點
- 優點: 簡單,好理解,好實現
- 缺點: 需要手寫兩份以上CSS配色樣式; 切換樣式需要下載CSS的時間
參考
web網頁中主題切換的實現思路 中有更多細節
3. 利用CSS預處理生成多套主題樣式
這是“準備多套CSS主題”的優化方案,利用CSS預處理生成多套主題樣式,再根據需要切換
- 利用Less,stylus 或 sass 的變量代替顏色值
- 配置多個主題顏色配置
- 利用webpack等工具輸出多套主題樣式
- 頁面加載后,根據用戶需求加載不同的樣式列表(同方案2)
優缺點
- 優點: 不用手寫兩套CSS
- 確定: 配置復雜;生成冗余的CSS文件; 切換樣式需要下載CSS的時間
參考
webpack的配置比較復雜,可以看這篇文章:webpack 換膚功能多主題/配色樣式打包解決方案
ant 環境下可以利用antd-theme-generator 快速配置,詳見:antd-theme-generator,antd在線換膚定制功能
4.動態換膚
這是element ui中的換膚方案,支持瀏覽器熱換膚。生成一套主題, 將主題配色配置寫在js中,在瀏覽器中用js動態修改style標簽覆蓋原有的CSS。
- 準備一套默認theme.css樣式
/* theme.css */
.title {
color: #FF0000
}
- 準備主題色配置
var colors = {
red: {
themeColor: '#FF0000'
},
blue: {
themeColor: '#0000FF'
}
}
- 異步獲取
theme.css
,將顏色值替換為關鍵詞
關鍵字可以確保以后能多次換色
var styles = ''
axios.get('theme.css').then((resp=> {
const colorMap = {
'#FF0000': 'themeColor'
}
styles = resp.data
Object.keys(colorMap).forEach(key => {
const value = colorMap[key]
styles = styles.replace(new RegExp(key, 'ig'), value)
})
}))
style 變為:
.title {
color: theme-color
}
- 把關鍵詞再換回剛剛生成的相應的顏色值,并在頁面上添加 style 標簽
// console 中執行 writeNewStyle (styles, colors.blue) 即可變色
function writeNewStyle (originalStyle, colors) {
let oldEl = document.getElementById('temp-style')
let cssText = originalStyle
// 替換顏色值
Object.keys(colors).forEach(key => {
cssText = cssText.replace(new RegExp(key, 'ig'), colors[key])
})
const style = document.createElement('style')
style.innerText = cssText
style.id = 'temp-style'
oldEl ? document.head.replaceChild(style, oldEl) :
document.head.appendChild(style) // 將style寫入頁面
}
此時style 變為:
.title {
color: '#0000FF'
}
優缺點
- 優點: 只需一套CSS文件; 換膚不需要延遲等候;可自動適配多種主題色;
- 缺點: 稍難理解; 需準確的css顏色值;可能受限于瀏覽器性能;
參考
本文最后有該方案的完整代碼
Vue 換膚實踐
elementUI 及 vuetifyjs動態換色實踐
vue-element-admin 動態換膚
webpack 插件抽取CSS中的主題色
5. CSS 變量換膚
利用CSS 變量設置顏色, 用js動態修改CSS變量,進而換色。
如果不考慮IE兼容,這是最佳換膚方案
看下面的例子,很好理解
<html>
<head>
<title>CSS varies</title>
<style>
:root {
--theme-color: red /* css 變量賦值位置 */
}
.title {
color: var(--theme-color) /* 用css變量標記顏色 */
}
</style>
</head>
<body>
<h3 class="title">CSS 變量換膚</h3>
<script>
// console 中執行 changceColor('blue') 即可變色
function changeColor(color = 'blue') {
document.documentElement.style.setProperty("--theme-color",color);
}
</script>
</body>
</html>
優缺點
- 優點:只需一套CSS文件; 換膚不需要延遲等候;對瀏覽器性能要求低;可自動適配多種主題色;
- 缺點: 不支持IE, 2016年前的chrome,safari; 兼容性參見Can I Use CSS Variables
參考
附A: 方案四 態換換膚完整代碼
dynamic.html
<html lang="en">
<head>
<title>js 動態換膚</title>
<!-- 利用axios 實現異步加載樣式-->
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
</head>
<body>
<h3 class="title">js 動態換膚</h3>
<script>
// 1. 主題顏色配置
var colors = {
red: {
themeColor: '#FF0000'
},
blue: {
themeColor: '#0000FF'
}
}
// 2. 異步獲取樣式
var styles = ''
axios.get('theme.css').then((resp=> {
const colorMap = {
'#FF0000': 'themeColor'
}
styles = resp.data
Object.keys(colorMap).forEach(key => {
const value = colorMap[key]
styles = styles.replace(new RegExp(key, 'ig'), value)
console.log(styles)
})
writeNewStyle (styles, colors.red)
}))
// 3.換色
// console.log 中輸入 writeNewStyle (styles, colors.blue)可以換藍色主題
// console.log 中輸入 writeNewStyle (styles, colors.blue)可以換紅色主題
function writeNewStyle (originalStyle, colors) {
let oldEl = document.getElementById('temp-style')
let cssText = originalStyle
Object.keys(colors).forEach(key => {
cssText = cssText.replace(new RegExp(key, 'ig'), colors[key])
})
const style = document.createElement('style')
style.innerText = cssText
style.id = 'temp-style'
oldEl ? document.head.replaceChild(style, oldEl) : document.head.appendChild(style)
}
</script>
</body>
</html>
theme.css
.title {
color: #FF0000
}