最近在使用vite+react + ant-design 來搭建個人站點,看到網上好多網站都實現了黑白面板的切換,並且ant-design幫我們實現了三套主題色,一個預設亮白色,暗黑主題和緊湊主題。於是我也想來弄一弄。最後還是實現了,打包後也是ok的。
對於網站需要切換主題的話,一般有以下幾種辦法。
- 使用
css
覆蓋的方式,由於css基於後面的css覆蓋前面的原理,所以這一點也是可以的。但是這一點對於使用less和scss的碼友來說,貌似不是一個很好的方法- 由於
less
裡面帶有一個less.js
的cdn
,可以用來解析是html種使用less檔案,但是這個需要注意使用的順序,需要less的樣式檔案在前面,less.js的參照在後面,這個對於使用構建工具的同志來說不太友好,打包後less檔案都不見了,直接使用路徑肯定也是不行。- 社群成熟的兩個庫:
antd-theme-webpack-plugin
和antd-theme-generator
,對於我的專案來說貌似都不怎麼合適,首先:antd-theme-webpack-plugin
這個庫是基於webpack來的,我們都知道vite是在開發環境使用esbuild
,生產環境使用的是roallup
來進行打包。antd-theme-generator
這個庫的話,把less提升到了執行階段,我們程式碼一般會進行打包壓縮等,如果使用這個庫的話就意味著需要設定less相關的靜態資源不能被打包,不然會有問題。
我的程式碼地址是(這個地址不會改動): https://github.com/cll123456/blog
將ant-design的兩個主題,預設主題和暗黑主題引入到我自己的less檔案中。然後對此就可以後序實現改動主題色,例如:成功,失敗,警告等。如下:
這個引入的順序需要注意,後引入的變數會覆蓋前面的。不會自定義的會不生效
switch
框的時候觸發方法。做以下嘗試所有的嘗試都是基於下面的第二步,也就是方法,這裡面需要做啥事情
改變方法的時候直接來動態引入less檔案,這樣在引入暗黑主題是可以實現的,但是從暗黑主題卻切換不過來了。如下:
const handleSkin = (checked: boolean) => {
if (checked) {
// 明亮主題
import('./../assets/style/index.less')
} else {
// 暗色主題
import('./../assets/style/index.dark.less')
}
}
這個從白的可以切換到黑的原因是,黑色樣式覆蓋了前面白色的樣式,但是如果你再一次覆蓋卻不行,我估計是選擇器權重問題上,ant官方做了改動。如果需要從新切換回來也是有辦法的,在明亮主題中直接
window.location.reload()
,這樣是可以切換回來的,如下圖:
這樣雖然實現了功能,體驗肯定是不好的,作為一名前端工程師,肯定是需要非常注重體驗的,不然職業生涯的路可能就不會很長。
- 由於嘗試一不行,然後我就往
import
動態引入這邊考慮了,我考慮的方向是既然可以動態import引入,那麼我可以再一次改變的時候把前一次引入的給remove
掉麼?- 但是我找遍了所有的檔案,import匯入的是無法
remove
掉的,import匯入是現代瀏覽器裡面的esm的語法。- 然後就去網上找各種方法,在ant-design pro中發現實現了這個功能,並且是無重新整理的,然後就去gitup上看人家的原始碼。功夫不負有心人,然後發現人家是動態使用
link引入css
的方式來實現的,那麼我也可以來通過link匯入less檔案來實現,並且使用less.js的cdn來進行解析。
新增一個addSkin的方法,畢竟需要匯入檔案,然後來查詢原來是否存在,然後進行刪除。
// 呼叫方法
const handleSkin = (checked: boolean) => {
if (checked) {
// 明亮主題
addSkin("./../../src/assets/style/index.less")
} else {
// 暗色主題
addSkin("./../../src/assets/style/index.dark.less")
}
}
// 新增面板的方法
function addSkin(path: string) {
let head = document.getElementsByTagName("head")[0];
const getLink = head.getElementsByTagName('link');
// 查詢link是否存在,存在的話需要刪除dom
if (getLink.length > 0) {
for (let i = 0, l = getLink.length; i < l; i++) {
if (getLink[i].getAttribute('data-type') === 'theme') {
getLink[i].remove();
}
}
}
// 查詢script是否存在
const getScript = head.getElementsByTagName('script');
if (getScript.length > 0) {
for (let i = 0, l = getScript.length; i < l; i++) {
if (getScript[i].getAttribute('data-type') === 'theme') {
getScript[i].remove();
}
}
}
// 最後加入對應的主題和載入less的js檔案
let link = document.createElement("link");
link.dataset.type = "theme";
link.href = path;
link.rel = "stylesheet";
link.type = "text/css";
head.appendChild(link);
// 這個less.js一定要放到後面才行
let script = document.createElement('script');
script.type = 'text/javascript';
script.dataset.type = 'theme';
script.src = 'https://cdn.bootcdn.net/ajax/libs/less.js/4.1.1/less.js'
head.appendChild(script)
}
這種方法是動態改變link標籤的樣式來實現的,在生產環境是沒有任何問題,但是在開發環境就不行了,打包後路徑不存在。肯定是不行的,接下來我就去找vite如何靜態資源複製到打包的檔案,方法找到了。但是我的less裡面參照了antd裡面的less,裡面的也不用打包? 我覺得不太好,因此再一次放棄。
既然直接使用
less
檔案不行,那我可以使用css
不,和ant-design pro
裡面一樣的,我也來參照css
檔案,接下來就往這個方向。
我直接列印了,import dark from './xxxx'.less
發現既然是一個字串。
是編譯好的字串,那我直接使用style
標籤就好了。說幹就往下幹。
import dark from './../assets/style/index.dark.less'
import lighter from './../assets/style/index.less'
// 呼叫方法
const handleSkin = (checked: boolean) => {
if (checked) {
// 明亮主題
addSkin(lighter)
} else {
// 暗色主題
addSkin(dark)
}
}
// 新增面板的方法
function addSkin(content: string) {
let head = document.getElementsByTagName("head")[0];
const getStyle = head.getElementsByTagName('style');
// 查詢style是否存在,存在的話需要刪除dom
if (getStyle.length > 0) {
for (let i = 0, l = getStyle.length; i < l; i++) {
if (getStyle[i].getAttribute('data-type') === 'theme') {
getStyle[i].remove();
}
}
}
// 最後加入對應的主題和載入less的js檔案
let styleDom = document.createElement("style");
styleDom.dataset.type = "theme";
styleDom.innerHTML = content;
head.appendChild(styleDom);
}
- 這裡有一個細節就是,樣式匯入必須在頂部匯入,不然vite會檢測不到,不能使用動態匯入,打包會經過
treeshake
去掉.- 其實這裡還有一個問題,那就是css打包後會比較大,畢竟引入了兩份,這個問題就留給碼友了,自己去
vite
獲取其他的構建工具(webpack, gulp
等)上找靜態資源太大怎麼處理。
在真實的偵錯中肯定是不止這三遍嘗試的,這裡只記錄走向成功的關鍵三步。More interest, less interests
(多一些興趣愛好的嚮往,少一些功名利祿的追求)