茶已备好,只待君来!感谢关注 前端点线面 (>‿<),本号定期推荐原创深度好文,帮助每一位在前端领域打拼的伙伴们走向前列,此外关注我获取最前沿知识点、海量学习资料、《前端百题斩》、大量思维导图,并进前端划水交流群

作者:令夕

原文链接:https://juejin.im/post/5e92ad7a518825736c5b91cd

最近在做网站换肤的需求,也就是主题切换。那么如何切换主题的颜色呢?以下是网站换肤的实现以及基于换肤拓展的一些方案分享给大家,希望大家在做类似需求的时候能够有些参考。

覆盖样式实现


// light
$color-brand1: #ffcd32;
$fill-1: #fff !default;
$color-text: #3c3c3c;
$color-text-1: #757575;
$color-text-2: #222;// dark
$dark-fill-1: #222 !default; // 品牌色
$dark-color-text: #fff;
$dark-color-text-1: rgba(255, 255, 255, 0.3);
$dark-color-text-2: $color-brand1;
// 页面使用
<style lang="scss">
@import "./assets/scss/index.scss";[data-theme="dark"] {body {background: $dark-fill-1;}.reaconmend .reaconmend-list .item .name {color: $dark-color-text;}.reaconmend .reaconmend-list .item .desc {color: $dark-color-text-1;}.header .text {color: $dark-color-text-2;}
}
</style>

利用css优先级的原理覆盖掉原有样式的实现,每定义一套皮肤就要定义对应的sass变量,以及定义一套覆盖原有样式的皮肤样式。如果有多套皮肤的话,覆盖的代码量就会n套。

缺点: 样式不易管理,查找样式复杂,开发效率低,拓展性差,维护成本高,多人协作沟通麻烦。

sass变量实现


// variable.scss  // 浅色
$colors-light: (fill-1: #fff,text: #3c3c3c,text-1: #757575,text-2: #222,
);// 深色
$colors-dark: (fill-1: #222,text: #fff,text-1: rgba(255, 255, 255, 0.3),text-2: #ffcd32,
);
// mixin.scss
// 背景色
@mixin bg-color($key) {background-color: map-get($colors-light, $key);[data-theme="dark"] & {background-color: map-get($colors-dark, $key);}
}
// text色
@mixin text-color($key) {color: map-get($colors-light, $key);[data-theme="dark"] & {color: map-get($colors-dark, $key);}
}
// 页面使用<style lang="scss" rel="stylesheet/scss">
@import "../../../assets/scss/variable.scss";
@import "../../../assets/scss/mixin.scss";
.reaconmend-list {.list-title {height: 40px;line-height: 40 px;text-align: center;@include text-color(text-1);}
}
</style>

如上所示用到的知识点包含Sass变量(variable),嵌套(nestend rules),混合(mixins), Sass Maps的函数-map-get(key)。

Maps的含义:Maps可视为键值对的集合,键被用于定位值 在css种没有对应的概念。和Lists不同Maps必须被圆括号包围,键值对被都好分割 。Maps中的keys和values可以是sassscript的任何对象。(包括任意的sassscript表达式 arbitrary SassScript expressions) 和Lists一样Maps主要为sassscript函数服务,如 map-get函数用于查找键值,map-merge函数用于map和新加的键值融合,@each命令可添加样式到一个map中的每个键值对。Maps可用于任何Lists可用的地方,在List函数中 Map会被自动转换为List , 如 (key1: value1, key2: value2)会被List函数转换为 key1 value1, key2 value2 ,反之则不能。(网友Soledad提供)

使用scss变量换肤相比覆盖样式

  • 拓展性更强

  • 将换肤的逻辑进行了收敛

生成多套皮肤css


使用覆盖样式实现与scss变量实现会把多套皮肤的样式都编译到一个css文件里面,如果有多套皮肤样式,这个文件是会非常大的。为了解决这样的问题,就自然的想出了拆分scss的实现:

实现方案,通过编译工具与构建工具编译出多套皮肤css,通过js动态的link对应的皮肤样式

// js动态处理var theme = /\bt=(\w+)/.exec(location.search);theme = theme ? theme[1] : "light";changeTheme(theme);function changeTheme(theme) {var head = document.getElementsByTagName("head")[0];var link = document.createElement("link");link.dataset.type = "theme";link.href = "assets/css/theme-" + theme + "/pages/home/home.css";link.rel = "stylesheet";link.type = "text/css";head.appendChild(link);
}

CSS变量实现


// variable.scss
// 默认变量
:root {--fill-1: #fff;--text: #3c3c3c;--text-1: #757575;--text-2: #222;--font-size-large: 18px;--font-size-large-x: 22px;--font-size-medium: 14px;--font-size-medium-x: 16px;--font-size-small-s: 10px;--font-size-small: 12px;
}
// 深色变量
[data-theme="dark"] {--fill-1: #222;--text: #fff;--text-1: rgba(255, 255, 255, 0.3);--text-2: #ffcd32;
}

在页面对css变量做引入使用

// 页面使用
@import "../../assets/scss/variable.scss";.header {position: relative;height: 70px;text-align: center;font-size: 0; .text {display: inline-block;vertical-align: top;line-height: 70px;font-size: var(--font-size-large);color: var(--text-2);}
}

具体的实现效果:

问题点:css变量会存在兼容性问题

css变量兼容性如下:虽然现在大部分主流浏览器都可以兼容,但是还要考虑更多的兼容性这块的请往下看:

CSS变量兼容性实现-1


在css变量的基础上新增了postcss-custom-properties这个插件 安装依赖:npm install postcss-custom-properties --save-dev npm install postcss-loader --save-dev

在根目录新建postcss.config.js增加配置,配置如下:

const postcssCustompProperties = require("postcss-custom-properties");module.exports = {plugins: [postcssCustompProperties({importFrom: "src/assets/scss/variable.scss"})]
};

postcss 会将css自定义变量直接编译为确定值,而不是保留。这时就需要 postcss 插件 来为我们保留这些自定义变量,使用 postcss-custom-properties效果如下:

  • 优点:会生成一套与css变量对应的css

  • 缺点:在构建时根据css变量生成对应的css,换肤是运行时并不能生成对应的css。

换肤后样式:

CSS变量兼容性实现-2


首先需要建一个存放公共css变量的js文件,将需要定义的css变量存放到该js文件,例如(variable.js)

// variable.js// 字体变量
const baseSize = {"--font-size-large-x": "22px","--font-size-large": "18px","--font-size-medium": "14px","--font-size-medium-x": "16px","--font-size-small-s": "10px","--font-size-small": "12px",
};//浅色
export const lightTheme = {"--fill-1": "#fff","--text": "#3c3c3c","--text-1": "#757575","--text-2": "#222",...baseSize,
};// 深色
export const darkTheme = {"--fill-1": "#222","--text": "#fff","--text-1": "rgba(255, 255, 255, 0.3)","--text-2": "#ffcd32",...baseSize,
};

页面使用css变量,例如:

<style lang="scss">.text {display: inline-block;vertical-align: top;line-height: 70px;font-size: var(--font-size-large);color: var(--text-2);}
</style>

安装css-vars-ponyfill 插件

css-vars-ponyfill官方概念:在传统浏览器和现代浏览器中为CSS自定义属性(又名“CSS变量”)提供客户端支持的ponyfill。(具体用法与概念请查阅官方网站:css-vars-ponyfill

封装切换主题的js,在main.js做初始化调用

// theme.js
import { lightTheme, darkTheme } from "../src/assets/js/variable";
import cssVars from "css-vars-ponyfill";
export const initTheme = (theme) => {document.documentElement.setAttribute("data-theme", theme ? "light" : "dark");cssVars({watch: true, // 当添加,删除或修改其<link>或<style>元素的禁用或href属性时,ponyfill将自行调用variables: theme ? lightTheme : darkTheme, // variables 自定义属性名/值对的集合onlyLegacy: false, // false  默认将css变量编译为浏览器识别的css样式  true 当浏览器不支持css变量的时候将css变量编译为识别的css});
};

在切换主题的按钮组件中调用

总结:css自定义属性 + css-vars-ponyfill(解决兼容性) 预览效果

细心的小伙伴们,一定发现了这里的css变量已经编译成浏览器可识别的css样式了。

ElementUI实现


官方的实现解释

  • 先把默认主题文件中涉及到颜色的 CSS 值替换成关键词:github.com/ElementUI/t…

  • 根据用户选择的主题色生成一系列对应的颜色值:github.com/ElementUI/t…

  • 把关键词再换回刚刚生成的相应的颜色值:github.com/ElementUI/t…

  • 直接在页面上加 style 标签,把生成的样式填进去:github.com/ElementUI/t…

已实现的链接参考:https://juejin.im/post/5ca41617f265da3092006155#heading-1

less在线编译实现


根据less可以直接 编译less变量实现的步骤如下:

// variable.less 定义less变量
// 公共字体
@font-size-large-x: 22px;
@font-size-large: 18px;
@font-size-medium: 14px;
@font-size-medium-x: 16px;
@font-size-small-s: 10px;
@font-size-small: 12px;// 浅色
@fill-1: #fff;
@text: #3c3c3c;
@text-1: #757575;
@text-2: #222;// 页面使用 例如:
// 下面.text的css 如下,这里的 @font-size-large 和  @text-2就是 less 变量:
.text {display: inline-block;vertical-align: top;line-height: 70px;font-size: @font-size-large;color: @text-2;}

当点击换肤按钮的时候,直接去加载 less.js,具体代码如下:

<template><div class="header"><div class="text">小恐龙换肤</div><div role="switch" class="switch" :class="theme === true ? 'is-checked' : ''"><input type="checkbox" class="switch-input" /><span class="switch-core" @click="changeTheme"></span></div></div>
</template><script>
import { lightTheme, darkTheme } from "../../assets/js/variable";
export default {name: "m-header",data() {return {theme: true};},methods: {changeTheme() {this.theme = !this.theme;// 调用 `less.modifyVars` 方法来改变变量值window.less.modifyVars(this.theme ? lightTheme : darkTheme);}},mounted() {}
};
</script>

定义variable.js是因为如果直接将less变量放在modifyVars中切换的效果只会生效一次,所以根据切换的状态使用对应的less变量。

//浅色
export const lightTheme = {"@fill-1": "#fff","@text": "#3c3c3c","@text-1": "#757575","@text-2": "#222",
};// 深色
export const darkTheme = {"@fill-1": "#222","@text": "#fff","@text-1": "rgba(255, 255, 255, 0.3)","@text-2": "#ffcd32",
};

然后点击色块进行试验,发现并没有生效,这是why?然后就去看了其文档,原来它会找到所有如下的less 样式标签,并且使用已编译的css同步创建 style 标签。也就是说我们必须吧代码中所有的less 都以下面这种link的方式来引入,这样less.js 才能在浏览器端实现编译。

<link rel="stylesheet/less" type="text/css" href="index.less" />

这里我用了vue,所以直接把 less 文件放在了public目录下,然后在html中直接引入:

点击切换按钮,可见background和color确实都变了

注:使用less 来实现换肤要注意 less 文件在 html 中编写的位置,不然很可能被其他css 文件所干扰导致换肤失败。如果less文件特别大,会存在编译性能问题。

拓展-图片切换


以上的方案---只是对background-color和color进行的换肤,如果要对图片进行换肤该怎么办呐?                                     

图片切换

项目中还存在很多占位图或者其他图片会随着主题的变化而变化。通过引入所有图片,并用文件名来区分不同主题所对应的图片。在点击切换主题时,切换到主题所对应的文件,就能实现图片切换了。

<img class :src="avatar" alt />

// 页面实现
<template><div class="header"><div class="text">小恐龙换肤</div><div role="switch" class="switch" :class="theme === true ? 'is-checked' : ''"><input type="checkbox" class="switch-input" /><span class="switch-core" @click="changeTheme"></span></div></div>
</template>
<script>
import { initTheme } from "../../theme";
import bus from "../../bus";
export default {name: "m-header",data() {return {theme: true, // false深色主题avatar: ""};},methods: {changeTheme() {this.theme = !this.theme;initTheme(this.theme);this.setThemeValue(this.theme);bus.$emit("changeTheme", this.theme);},setThemeValue(theme) {theme = theme ? "light" : "dark";this.avatar = require(`@/assets/images/logo-${theme}.jpeg`);}},created() {this.setThemeValue(this.theme);}
};
</script>

在点击切换主题时,会发射一个 changeTheme 事件,各组件接收到 changeTheme 事件,就会为图片重新赋值,也就达到了切换图片的效果。

最后


很感谢能在百忙中抽出时间把这篇文章看完的小伙伴:) 如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。好了,本文到此结束,希望对你有帮助 :)

················ 执鸢者简介 ·················

大家好,我是执鸢者,毕业于华中科技大学,新时代农民工,现在是百度前端研发工程师,著有《前端百题斩》、数十篇学习思维导图(go、React、Redux、Vue、Vuex、操作系统、Linux、设计模式、js、webpack、nginx)以及大量前端进阶文章,大量同学已通过号主的系列内容获取心仪的offer,关注我获取海量资料、交流工作心得并进卧虎藏龙交流群。

识别方二维码加我微信、拉你进交流

[1] 五万字前端面试宝典

[2] 纯CSS实现beautiful按钮

[3] 一张思维导图入门React

[4] 一文搞定Diff算法

[5] 16张图入门Nginx

[6] 好记性不如烂笔头——Vue3.0篇

[7] 好记性不如烂笔头——Vuex篇

[8] 好记性不如烂笔头——Linux篇

[9]好记性不如烂笔头——React篇

[10] 好记性不如烂笔头——Redux篇

前端换肤的N种方案,请收下相关推荐

  1. 前端 “一键换肤“ 的几种方案

    作者:熊的猫 原文:https://juejin.cn/post/7063010855167721486 前端 "一键换肤" 的几种方案 前言 现在越来越多的网站都提供了拥有换肤( ...

  2. 前端 “一键换肤“ 的 N 种方案

    前端瓶子君,关注公众号 回复算法,加入前端编程面试算法每日一题群 前言 现在越来越多的网站都提供了拥有换肤(切换主题)功能,如 ElementUI[2],既是为了迎合用户需求,或是为了凸显自己特点,因 ...

  3. 前端 一键换肤 的几种方案

    现在越来越多的网站都提供了拥有换肤(切换主题)功能,如 ElementUI,既是为了迎合用户需求,或是为了凸显自己特点,因此提供了个性化定制的功能. 其实之前就想了解和实现 "一键换肤&qu ...

  4. -----前端换肤----

    ------前端换肤------ 1.前端的样式全局布局的情况下,通过定义全局变量统一底色,我是在app.vue中定义的色值: <style>body {--generalcolor: # ...

  5. 计算机主机配置一般有机箱主板cpu,现在的电脑主流配置_现在电脑CPU主流配置哪几种,请说明下它们的...

    现在的主流配置电脑怎么弄 CPU+主板 AMD 3600+B450M 1900 内存 DDR4 3000 8GX2 600 硬盘 英特尔256G ssd M.2(NVMe) 390 显卡 GTX166 ...

  6. 花一千多学python值吗_Python爬虫应该怎么学?程序猿花了一周整理的学习技巧,请收下...

    原标题:Python爬虫应该怎么学?程序猿花了一周整理的学习技巧,请收下 Python爬虫为什么受欢迎 如果你仔细观察,就不难发现,懂爬虫.学习爬虫的人越来越多,一方面,互联网可以获取的数据越来越多, ...

  7. 软件测试公司折扣政策,@软件企业看,这些税收优惠政策请收下

    原标题:@软件企业看,这些税收优惠政策请收下 软件是信息技术之魂.网络安全之盾.经济转型之辙.数字社会之基,软件产业正以其强大的技术创新力日益成为数字经济发展的"领跑团".记者从苏 ...

  8. 羊皮纸背景计算机,整天面对屏幕的你,请收下这几个护眼小技巧

    原标题:整天面对屏幕的你,请收下这几个护眼小技巧 现代办公,电脑已经成为必不可少的工具 "眼睛干涩""看东西模糊""腰酸背痛脖子疼" 很多人 ...

  9. KeyError: [] not found in axis_最IN黄浦 | 万圣狂欢倒计时!露台派对、变装游、沉浸式密室...请收下这份黄浦活动指南~_电竞...

    原标题:最IN黄浦 | 万圣狂欢倒计时!露台派对.变装游.沉浸式密室...请收下这份黄浦活动指南~ Ttick or Treat 一年一度的万圣节就要来了, 黄浦不少商圈都策划了多场精彩活动: BFC ...

  10. 知名演员从北大毕业!学位论文让网友直呼:请收下我的膝盖!

    来源:广州日报 编辑:双一流高校 近日,49岁香港男艺人马浚伟发布微博称,自己已通过北京大学光华管理学院硕士研究生学位论文答辩,顺利毕业. 相关的一则话题达到了1200万的阅读量,超7000名网友参与 ...

最新文章

  1. 【机器学习】多项式回归原理介绍
  2. hdu5094(上海邀请赛E) 状态压缩bfs:取钥匙开门到目的地
  3. Python+selenium 自动化-切换窗口页签、切换iframe框架。确定页面是否包含iframe方法。
  4. python中configparser详解_python ConfigParser模块详解
  5. 编译原理lr(0)c语言,关于编译原理:LR(1)LR(0)文法判断?
  6. 淘宝评价网----类反淘宝联盟上线
  7. mongdb2008puls4.0.3安装_Mathematica 10.3安装教程
  8. win7系统如何共享硬盘分区
  9. checkbox的全选与反选
  10. OC-引用计数器,内存管理,野指针
  11. RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势...
  12. jQuery特效:实现微博发布界面
  13. VSTOhowtoreferene.Net3.5(VSTO如何引用.NET3.5的外接程序)
  14. 解决应用MSBuild编译项目没有拷贝间接引用的dll题目
  15. python游戏计分代码_Python笔试题之设计“跳一跳”小游戏计分器
  16. Gmail注册时手机号无法验证
  17. MetaLife与ESTV建立战略合作伙伴关系并任命其首席执行官Eric Yoon为顾问
  18. Android Studio 连接逍遥模拟器
  19. drm debug log with drm.debug=6 debug
  20. Android Studio 单击按钮放大字体

热门文章

  1. openwrt luci web解析
  2. 如何用html来编写ppt,impress.js——用HTML“写”幻灯片
  3. 打印机无法打印的一些解决办法
  4. 指针进阶:函数指针的应用场景
  5. L13:数据结构-5(树和二叉树)
  6. matlab仓库选址,物流中心选址matlab
  7. python转换字符编码_python 字符串编码转换
  8. 基金经理学量化(Python+AI)
  9. drony+fiddler抓包
  10. 百度爬虫robots.txt文件规范