背景
實(shí)現(xiàn)主題切換有幾種不同的方案载荔,比如使用CSS變量,使用JavaScript動態(tài)加載對應(yīng)的主題樣式文件等翘狱。本文主要講的是如何使用Sass實(shí)現(xiàn)主題切換鼎兽。
前置知識
了解Sass的基本使用
- variable
- mixin
- map
本質(zhì)
Sass作為CSS預(yù)處理器,需要編譯成CSS后漓糙,才能被瀏覽器識別和解析铣缠。因此無法在瀏覽器中直接使用Sass實(shí)現(xiàn)類似CSS變量那種動態(tài)切換。本質(zhì)上來說昆禽,項(xiàng)目中有幾個主題就要提前定義好幾份主題樣式并全部引入蝗蛙。
思路
首先,我們需要給應(yīng)用的頂層元素添加一個主題標(biāo)識醉鳖,用于標(biāo)識當(dāng)前的主題捡硅,用于之后應(yīng)用上對應(yīng)的主題樣式。該標(biāo)識可以是數(shù)據(jù)屬性辐棒,也可以是類病曾,也可以是id牍蜂,這里采用數(shù)據(jù)屬性。
<html>
<div class="app" data-theme="light"></div>
</html>
然后泰涂,每次切換主題時(shí)鲫竞,通過更新該標(biāo)識,頁面就會應(yīng)用樣式文件中提前定義好的對應(yīng)的主題樣式逼蒙。
.app {
&[data-theme='light'] {
color: #333;
}
&[data-theme='dark'] {
color: #fff;
}
}
實(shí)現(xiàn)
基礎(chǔ)版
基于主題切換的本質(zhì)和思路从绘,我們可以通過硬編碼,實(shí)現(xiàn)一個簡單的主題切換是牢。
<div id="app" class="app">
<h1 class="title">Hello, World</h1>
<p class="subtitle">當(dāng)前主題:<span id="theme-current">亮色</span></p>
<button class="theme-switch light" data-theme="light">亮色</button>
<button class="theme-switch dark" data-theme="dark">暗色</button>
</div>
首先給應(yīng)用添加一個主題標(biāo)識僵井,這里我通過給body
元素添加一個數(shù)據(jù)屬性data-theme
表示當(dāng)前的主題。默認(rèn)為light
<body data-theme="light">
<div class="app"></div>
</body>
然后提前定義好所有主題樣式:
// 所有主題樣式
$bg-color-light: #ffffff;
$bg-color-dark: #091a28;
$title-color-light: #363636;
$title-color-dark: #ffffff;
$subtitle-color-light: #4a4a4a;
$subtitle-color-dark: cyan;
.app {
// 默認(rèn)主題樣式(light主題)
background-color: $bg-color-light;
// dark主題
[data-theme='dark'] & {
background-color: $bg-color-dark;
}
}
.title {
color: $title-color-light;
[data-theme='dark'] & {
color: $title-color-dark;
}
}
.subtitle {
color: $subtitle-color-light;
[data-theme='dark'] & {
color: $subtitle-color-dark;
}
}
最后驳棱,當(dāng)我們點(diǎn)擊不同主題按鈕時(shí)批什,就會更新body
上的主題標(biāo)識data-theme
,這樣社搅,樣式文件中對應(yīng)的主題樣式就會被應(yīng)用上了驻债。
完整代碼和實(shí)現(xiàn)效果可以參考Codepen:
CodePen: SASS實(shí)現(xiàn)主題換膚/主題切換-基礎(chǔ)版
不過該實(shí)現(xiàn)有點(diǎn)粗糙,存在幾個小問題:
-
每個需要應(yīng)用主題樣式的CSS選擇器中形葬,都要寫一遍對應(yīng)主題需要的樣式合呐,比較繁瑣
-
如果有多個主題,代碼量會極具增加笙以,并且很多都是重復(fù)的“模板代碼”
針對這些問題淌实,我們可以利用Sass的一些特性實(shí)現(xiàn)一個進(jìn)階版的主題切換。
進(jìn)階版
首先猖腕,針對基礎(chǔ)版暴露出的問題拆祈。我們需要對Sass變量做一點(diǎn)小小的調(diào)整。這里我們將主題樣式封裝成了map格式谈息,map中每一個元素都對應(yīng)著不同主題下的樣式缘屹。
// 所有主題樣式
$bg-color: (
// 亮色
light: #fff,
// 暗色
dark: #091a28
);
$title-color: (
light: #363636,
dark: #ffffff
);
$subtitle-color: (
light: #4a4a4a,
dark: cyan
);
針對重復(fù)的模版代碼和代碼繁瑣的問題,Sass中有個特性mixin
侠仇,正好可以利用上轻姿。
接下來,我們要封裝一個mixin逻炊,專門解決基礎(chǔ)版1-手寫代碼的繁瑣的問題互亮。
這里使用了Sass中的插值表達(dá)#{}
和map-get
方法,#{}
類似于JavaScript中的計(jì)算屬性余素,可以動態(tài)設(shè)置屬性名豹休,map-get
方法用于從map中獲取某一個屬性對應(yīng)的值吕世。
@mixin themify($key, $valueMap) {
// 默認(rèn)主題
#{$key}: map-get($valueMap, 'light');
// dark主題
[data-theme='dark'] & {
#{$key}: map-get($valueMap, 'dark');
}
}
themify
主要封裝了默認(rèn)主題樣式light
劫扒,和dark
主題樣式,這樣我們在選擇器里,只需要include
這些樣式即可味廊。
.app {
@include themify('background-color', $bg-color);
}
.title {
@include themify('color', $title-color);
}
.subtitle {
@include themify('color', $subtitle-color);
}
現(xiàn)在看這些代碼是不是簡潔多了榴都?省去了自己手寫那些繁瑣的模板代碼扳抽!
針對“多主題模版代碼會更多”的問題译打,解決起來也就很容易了。只需要簡單修改下該mixin留美,添加上對應(yīng)的主題樣式即可彰檬。
@mixin themify($key, $valueMap) {
// light主題
#{$key}: map-get($valueMap, 'light');
// dark主題
[data-theme='dark'] & {
#{$key}: map-get($valueMap, 'dark');
}
// dark1主題
[data-theme='dark1'] & {
#{$key}: map-get($valueMap, 'dark1');
}
// dark2主題
[data-theme='dark2'] & {
#{$key}: map-get($valueMap, 'dark2');
}
}
當(dāng)然,我們還可以對mixin做一下優(yōu)化谎砾,可以將主題封裝成list格式逢倍,然后通過遍歷主題,簡化mixin:
@mixin themify($key, $valueMap) {
// theme list
$themes: light, dark;
@each $theme in $themes {
[data-theme=#{$theme}] & {
#{$key}: map-get($valueMap, $theme);
}
}
}
這樣看起來就清爽多了景图。
完整代碼和實(shí)現(xiàn)效果可以參考Codepen:
CodePen: SASS實(shí)現(xiàn)主題換膚/主題切換-進(jìn)階版
總結(jié)
Sass作為一款流行的CSS預(yù)處理器较雕,提供了插值表達(dá)#{}
和map
類型等特性,在實(shí)現(xiàn)主題切換方面提供了不少便利症歇。
當(dāng)然郎笆,Sass實(shí)現(xiàn)主題切換還有很多可以優(yōu)化的點(diǎn)谭梗。這里隨便列兩條常見的:
-
如果有多條主題樣式需要應(yīng)用忘晤,每一條都要寫一遍
@include
,感覺有點(diǎn)麻煩激捏,能不能只寫一遍@include
设塔?.app { @include themify('background-color', $bg-color); @include themify('color', $text-color); // ... } // 希望可以只寫一遍@include .app { @include themify( ( 'background-color': $bg-color, 'color': $text-color ) ); }
-
如果還需要使用
!important
去覆蓋一些因?yàn)闄?quán)重問題無法應(yīng)用的樣式(比如使用了外部UI庫,外部UI庫中使用了!important
远舅,需要覆蓋該樣式)闰蛔,怎么解決?// 這里提供一個思路图柏,可以添加一個參數(shù)$important @mixin themify($key, $valueMap: null, $important: false) { // xxx }