Vue 網(wǎng)站切換主題,看這篇就夠了!

前言

更換主題的需求在移動端甚至一些有特殊需求的 pc 端都很常見了,大部分情況是由用戶點擊操作進行切換的

但如果,我們可以做到跟隨系統(tǒng)當前是 Light 還是 Dark 來自適應網(wǎng)頁的主題,看上去是不是很酷?

在這里,你可以 get 到

  1. 如何基于原生的 css variable 來進行主題更換?
  2. 怎么感知系統(tǒng)的主題切換并且觸發(fā)主題更換?
  3. 如何設計遠程動態(tài)主題加載方案,以適應用戶自定義主題配置?

想搶先看效果的看這里:https://ztstory.github.io/vue-theme/#/

思考與實踐

1、如何基于原生的 css 變量來進行主題更換?

說到前端的切換主題方案,有提到用less variable、sass variable、css variable等等,也都是比較成熟的方案了,我們主要聊一聊以 css variable為主的主題切換方案

CSS 變量:https://developer.mozilla.org/zh-CN/docs/Web/CSS/--*

利用變量的特性,我們可以比較容易的定義出我們的基礎主題樣式 Light

// 定義基礎色值,方便 UI 后期替換
:root {
    --zt-c-white: #ffffff;
    --zt-c-black: #181818;
    --zt-c-primary: rgb(56, 173, 122);

    --zt-c-text-light-1: #333;
    --zt-c-text-light-2: #666;
    --zt-c-text-dark-1: var(--zt-c-white);
    --zt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
// Light 默認主題色
:root {
    --color-background: var(--zt-c-white);
    --color-heading: var(--zt-c-primary);
    --color-text: var(--zt-c-text-light-1);
    --color-text-2: var(--zt-c-text-light-2);
}

再者,我們要準備一套 Dark 主題色

:root {
    --color-background: var(--zt-c-black);
    --color-heading: var(--zt-c-primary);
    --color-text: var(--zt-c-text-dark-1);
    --color-text-2: var(--zt-c-text-dark-2);
}

準備好之后,問題來了:我們怎么切換這兩套變量呢?

目前來看是相互覆蓋的,所以我們要借助 css 的優(yōu)先級來進行區(qū)別命中

我們只需要在 body 標簽上增加一個自定義屬性theme-mode="dark"

然后通過這個屬性值來匹配不同 tag 的主題色變量就完成基本的主題切換了

其他主題色切換同理

// 上面的代碼可以改造成這樣 .css
:root body[theme-mode="dark"] {
    ...
}

// .js
document.body.setAttribute("theme-mode", "dark");

為了視覺上絲滑一點,我們可以給 body 增加點過渡動畫,具體效果看上面的 demo 入口

body {
    min-height: 100vh;
    color: var(--color-text);
    background: var(--color-background);
    transition: color 0.5s, background-color 0.5s;
}

2、如何感知系統(tǒng)的主題切換自動更換主題?

這里就要介紹一下 prefers-color-scheme 這個新的 css 特性,可能有很多小伙伴已經(jīng)知道了,但也可能有很多小伙伴不知道,這里簡單介紹一下。

prefers-color-schemecss 媒體特性 @media 中用于檢測用戶是否有將系統(tǒng)的主題色設置為亮色或者暗色。

語法 描述
light 表示用戶已告知系統(tǒng)他們選擇使用淺色主題的界面。
dark 表示用戶已告知系統(tǒng)他們選擇使用暗色主題的界面。
no-preference 表示系統(tǒng)未得知用戶在這方面的選項。在 boolean 值上下文中,執(zhí)行結(jié)果為 false

若結(jié)果為 no-preference,無法通過此媒體特性獲知宿主系統(tǒng)是否支持設置主題色,或者用戶是否主動將其設置為無偏好。

出于隱私保護等方面的考慮,用戶或用戶代理也可能在一些情況下在瀏覽器內(nèi)部將其設置為 no-preference。

借助這個特性我們可以將上述代碼改造一下

@media (prefers-color-scheme: dark) {
    :root {
        --color-background: var(--zt-c-black);

        --color-heading: var(--zt-c-primary);
        --color-text: var(--zt-c-text-dark-1);
        --color-text-2: var(--zt-c-text-dark-2);
    }
}

這樣就可以保證系統(tǒng)切換暗黑模式時,我們的頁面同樣也變?yōu)榘岛谀J嚼瞺

3. 如何設計遠程動態(tài)主題加載方案,以適應用戶自定義主題配置?

在做完上面的事情之后,我們基本上已經(jīng)可以做到既能手動切換主題,也能自動切換主題了

但是,我又萌生了一個想法,如果是用戶自定義主題上傳,然后使用的話,那我們該怎么設計這個系統(tǒng)?

首先,我們要滿足用戶自定義主題,就得先將主題設置的變量都定義好,讓用戶需要按照我們規(guī)定的一些變量來進行創(chuàng)作,同時也方便代碼維護

變量我們就以上面定義的背景色及文字顏色來表示,自定義主題也基本圍繞著這幾種變量來設計

其次,我們需要實現(xiàn)遠程動態(tài)加載主題的方案

我的思路是利用動態(tài)添加 <link rel='stylesheet' href='xxx' />的方式

這個方案需要注意避免兩個問題:

  1. 設置主題的過程中需保證主題資源 link 不會重復添加
    • 不然真的會有很多很多個 link 標簽出現(xiàn),別問我怎么知道的
  2. 新舊主題資源切換時需注意控制主題過渡
    • 開始沒注意,沒等新主題資源加載完成就刪除了舊主題,導致頁面瞬間變回默認的 Light 主題

最后,我們也要保證可以在遠程主題與本地主題的刷新保留問題

前面的工作做好,我們已經(jīng)可以實現(xiàn)各種姿勢的主題切換了(代碼可以看文末的 Demo)

但還有個問題沒解決,就是刷新之后主題又會回歸原樣。

我的想法是themeTag 持久化儲存,每次刷新時同步該主題,這樣基本上就保證了主題的一致性

但如果是遠程加載的主題的話,還是存在刷新后 link 標簽丟失,需要重新添加的情況,所以會存在一定的體驗問題,這個也希望各位看客們可以給一些意見共同討論一下怎么設計比較好~

Demo

示例:https://ztstory.github.io/vue-theme/#/

源碼:https://github.com/ZTStory/vue-theme

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容