一文总结前端换肤换主题

最近项目涉及换主题/换肤的工作, 查了查资料,总结出五种换肤方案:

序号 方法 特点
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阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342