一文總結前端換膚換主題

最近項目涉及換主題/換膚的工作, 查了查資料,總結出五種換膚方案:

序號 方法 特點
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,代碼容易混亂;需手動編寫

參考

基于element動態換膚

2.準備多套CSS主題

本地存放多套主題, 根據需要切換加載不同的樣式

  1. 準備份默認樣式主題
/*theme-default.css*/
p {
  color: #333
}
...
  1. 準備各主題的樣式
/* theme-red.css */
p {
  color: #red
}
...
/* theme-blue.css */
p {
  color: #blue
}
...
  1. 頁面加載后,根據用戶需求加載不同的樣式列表
   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);
  1. 有時候需要保存用戶使用的主題,可以通過如下方式:
    • 利用路由標記
    • 利用cookie標記
    • 利用localstorage
    • 保存到后端服務器

優缺點

  • 優點: 簡單,好理解,好實現
  • 缺點: 需要手寫兩份以上CSS配色樣式; 切換樣式需要下載CSS的時間

參考

web網頁中主題切換的實現思路 中有更多細節

3. 利用CSS預處理生成多套主題樣式

這是“準備多套CSS主題”的優化方案,利用CSS預處理生成多套主題樣式,再根據需要切換

  1. 利用Less,stylus 或 sass 的變量代替顏色值
  2. 配置多個主題顏色配置
  3. 利用webpack等工具輸出多套主題樣式
  4. 頁面加載后,根據用戶需求加載不同的樣式列表(同方案2)

優缺點

  • 優點: 不用手寫兩套CSS
  • 確定: 配置復雜;生成冗余的CSS文件; 切換樣式需要下載CSS的時間

參考

webpack的配置比較復雜,可以看這篇文章:webpack 換膚功能多主題/配色樣式打包解決方案
ant 環境下可以利用antd-theme-generator 快速配置,詳見:antd-theme-generatorantd在線換膚定制功能

4.動態換膚

這是element ui中的換膚方案,支持瀏覽器熱換膚。生成一套主題, 將主題配色配置寫在js中,在瀏覽器中用js動態修改style標簽覆蓋原有的CSS。

  1. 準備一套默認theme.css樣式
/* theme.css */
.title {
  color: #FF0000
}
  1. 準備主題色配置
var colors = {
     red: {
       themeColor: '#FF0000'
     },
     blue: {
       themeColor: '#0000FF'
     }
   }
  1. 異步獲取 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
}
  1. 把關鍵詞再換回剛剛生成的相應的顏色值,并在頁面上添加 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
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374