likes
comments
collection
share

前端多主题切换

作者站长头像
站长
· 阅读数 62

前言:

demo,各位可以clone 本地查看下效果。最近在参加一个超好用的开源图床工具--picx开发。承接了暗黑主题这一个功能。正好也参加了 崔学社,每月一篇的任务。那么就来水一篇吧。

技术选择

前端目前来看 vue/react/angular/svelte..;less/scss/styles/css;不可能每一种 都去实现一种,那么有没有共同点呢---css。准确来说 css3 var;当然如果你使用的 ui 库还不是 var 变量,那么也可以结合使用。

考察优秀网站实现方式:配置式

祖师爷都推荐的 ui 组件库--varlet;在首页我们可以看到有切换主题的按钮,那么我们打开控制台看看到底做了什么; 前端多主题切换 我们可以看到它是通过切换 html 设置 style 的方式,也就是 css var :root 的效果来达到切换主题的。

下面我们去它源码里面看看实现方式

  1. 先通过那个切换图标来全局搜索到代码位置: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

  1. 创建 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;
}
  1. 创建切换主题工具函数 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 就可以达到切换主题的功能。

第三种

张鑫旭前辈基于原生link属性来切换皮肤

总结

三种方式各有利弊,没有垃圾的代码,只有不合理的使用。就像老婆,适合自己的才是最好的。

转载自:https://juejin.cn/post/7044812712487944200
评论
请登录