前端多主题切换
前言:
demo,各位可以clone 本地查看下效果。最近在参加一个超好用的开源图床工具--picx开发。承接了暗黑主题这一个功能。正好也参加了 崔学社,每月一篇的任务。那么就来水一篇吧。
技术选择
前端目前来看 vue/react/angular/svelte..;less/scss/styles/css;不可能每一种 都去实现一种,那么有没有共同点呢---css。准确来说 css3 var;当然如果你使用的 ui 库还不是 var 变量,那么也可以结合使用。
考察优秀网站实现方式:配置式
祖师爷都推荐的 ui 组件库--varlet;在首页我们可以看到有切换主题的按钮,那么我们打开控制台看看到底做了什么; 我们可以看到它是通过切换 html 设置 style 的方式,也就是 css var :root 的效果来达到切换主题的。
下面我们去它源码里面看看实现方式
- 先通过那个切换图标来全局搜索到代码位置:AppHeader.vue
const setCurrentThemes = (themes: 'themes' | 'darkThemes') => {
currentThemes.value = themes
setThemes(config, currentThemes.value)
window.localStorage.setItem(themesKey, currentThemes.value)
}
export function setThemes(config: Record<string, any>, name: 'themes' | 'darkThemes') {
const themes = get(config, name, {})
const styleVars = Object.entries(themes).reduce((styleVars, [key, value]) => {
styleVars[`--site-config-${key}`] = value as string
return styleVars
}, {} as StyleVars)
StyleProvider(styleVars)
}
function StyleProvider(styleVars: StyleVars | null = {}) {
mountedVarKeys.forEach((key) => document.documentElement.style.removeProperty(key))
mountedVarKeys.length = 0
const styles: StyleVars = formatStyleVars(styleVars)
Object.entries(styles).forEach(([key, value]) => {
document.documentElement.style.setProperty(key, value)
mountedVarKeys.push(key)
})
}
从代码里可以推断出,主题来源在config:varlet.default.config.js; 这种配置文件的方式好处也十分明显: 方便 js 操作。如果有这种需求完全可以采取这种方式。
另外一种方式:css文件配置各主题不同class
- 创建 bass.css(less/styl/scss) 都可以
// ========================================================================================
// .light-mode
// ========================================================================================
.light-mode {
--primary-color: #0066CC;
--background-color: #fff;
}
// ========================================================================================
// dark mode color
// ========================================================================================
.dark-mode {
--primary-color: #0066CC;
--background-color: #383940;
}
- 创建切换主题工具函数 useThemeChange.js
import { watch, onMounted, onUnmounted, nextTick } from 'vue'
import { useStore } from 'vuex'
const useThemeChange = () => {
const store = useStore()
const setBodyClassName = (theme) => {
nextTick(() => {
const body = document.getElementsByTagName('body')[0]
if (theme === 'dark') {
body.classList.remove('light-mode')
body.classList.add('dark-mode')
} else {
body.classList.remove('dark-mode')
body.classList.add('light-mode')
}
})
}
watch(
() => store.state.mode,
(newValue) => {
setBodyClassName(newValue)
},
{ deep: true }
)
const media = window.matchMedia('(prefers-color-scheme:dark)')
// 监听样式切换
const callback = (e) => {
const prefersDarkMode = e.matches
if (prefersDarkMode) {
// console.log('黑暗模式')
setBodyClassName('dark')
} else {
// console.log('亮色模式')
setBodyClassName('light')
}
}
onMounted(() => {
setBodyClassName(store.state.mode);
media.addEventListener('change', callback)
})
onUnmounted(() => {
media.removeEventListener('change', callback)
})
}
export default useThemeChange
只要在其它地方触发 vuex store.state.mode 就可以达到切换主题的功能。
第三种
总结
三种方式各有利弊,没有垃圾的代码,只有不合理的使用。就像老婆,适合自己的才是最好的。
转载自:https://juejin.cn/post/7044812712487944200