首页 > 其他分享 >Vue3主题切换

Vue3主题切换

时间:2024-06-06 13:57:57浏览次数:23  
标签:CacheKey const NAME 主题 value export 切换 Vue3 localStorage

又是一个曾经研究失败的课题,嘻嘻,今天必拿下~

网上有很多主题切换的案例,但是别人的终究是别人的,研究透彻你才能灵活运用。这边用的V3 admin vite项目的主题切换。

这边cv了相关组件后报错

在这个函数时发生了错误,为了提升效率,我直接将该函数的定义调到调用layouts中,报错消失

基本页面是实现了,但是切换没有效果。继续看看代码~

打印了一些关键节点,发现源代码的APP.vue有初始化,而我直接把按钮页放到了APP.vue导致没有初始化~加进去发现可以生效了

整理一下代码,再新建一个vue项目:

安装sass elementplus elementicon等组件

功能按键

<script lang="ts" setup>
import { type ThemeName, useTheme } from '@/hooks/useTheme'
import { MagicStick } from '@element-plus/icons-vue'

const { initTheme } = useTheme()

/** 初始化主题 */
initTheme()

/** 初始化主题 */
const { themeList, activeThemeName, setTheme } = useTheme()

const handleChangeTheme = ({ clientX, clientY }: MouseEvent, themeName: ThemeName) => {
  const maxRadius = Math.hypot(
    Math.max(clientX, window.innerWidth - clientX),
    Math.max(clientY, window.innerHeight - clientY)
  )
  const style = document.documentElement.style
  style.setProperty('--v3-theme-x', clientX + 'px')
  style.setProperty('--v3-theme-y', clientY + 'px')
  style.setProperty('--v3-theme-r', maxRadius + 'px')
  const handler = () => {
    setTheme(themeName)
  }
  // @ts-expect-error
  document.startViewTransition ? document.startViewTransition(handler) : handler()
}
</script>

<template>
  <el-dropdown trigger="click">
    <div>
      <el-tooltip effect="dark" content="主题模式" placement="bottom">
        <el-icon :size="20">
          <MagicStick />
        </el-icon>
      </el-tooltip>
    </div>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item
          v-for="(theme, index) in themeList"
          :key="index"
          :disabled="activeThemeName === theme.name"
          @click="
            (e) => {
              handleChangeTheme(e, theme.name)
              console.log(e)
            }
          "
        >
          <span>{{ theme.title }}</span>
        </el-dropdown-item>
      </el-dropdown-menu>
    </template>
  </el-dropdown>
</template>

usetheme

import { ref, watchEffect } from "vue"
import { getActiveThemeName, setActiveThemeName } from "@/utils/cache/local-storage"

const DEFAULT_THEME_NAME = "normal"
type DefaultThemeName = typeof DEFAULT_THEME_NAME

/** 注册的主题名称, 其中 DefaultThemeName 是必填的 */
export type ThemeName = DefaultThemeName | "dark" | "dark-blue"

interface ThemeList {
  title: string
  name: ThemeName
}

/** 主题列表 */
const themeList: ThemeList[] = [
  {
    title: "默认",
    name: DEFAULT_THEME_NAME
  },
  {
    title: "黑暗",
    name: "dark"
  },
  {
    title: "深蓝",
    name: "dark-blue"
  }
]

/** 正在应用的主题名称 */
const activeThemeName = ref<ThemeName>(getActiveThemeName() || DEFAULT_THEME_NAME)

/** 设置主题 */
const setTheme = (value: ThemeName) => {
  activeThemeName.value = value
}

/** 在 html 根元素上挂载 class */
const setHtmlRootClassName = (value: ThemeName) => {
  document.documentElement.className = value
}

/** 初始化 */
const initTheme = () => {
  // watchEffect 来收集副作用
  watchEffect(() => {
    const value = activeThemeName.value
    console.log(value)
    setHtmlRootClassName(value)
    setActiveThemeName(value)
  })
}

/** 主题 hook */
export function useTheme() {
  return { themeList, activeThemeName, initTheme, setTheme }
}

getsettheme

/** 统一处理 localStorage */

import CacheKey from "@/constants/cache-key"
import { type SidebarOpened, type SidebarClosed } from "@/constants/app-key"
import { type ThemeName } from "@/hooks/useTheme"
import { type TagView } from "@/store/modules/tags-view"
import { type LayoutSettings } from "@/config/layouts"

//#region 系统布局配置
export const getConfigLayout = () => {
  const json = localStorage.getItem(CacheKey.CONFIG_LAYOUT)
  return json ? (JSON.parse(json) as LayoutSettings) : null
}
export const setConfigLayout = (settings: LayoutSettings) => {
  localStorage.setItem(CacheKey.CONFIG_LAYOUT, JSON.stringify(settings))
}
export const removeConfigLayout = () => {
  localStorage.removeItem(CacheKey.CONFIG_LAYOUT)
}
//#endregion

//#region 侧边栏状态
export const getSidebarStatus = () => {
  return localStorage.getItem(CacheKey.SIDEBAR_STATUS)
}
export const setSidebarStatus = (sidebarStatus: SidebarOpened | SidebarClosed) => {
  localStorage.setItem(CacheKey.SIDEBAR_STATUS, sidebarStatus)
}
//#endregion

//#region 正在应用的主题名称
export const getActiveThemeName = () => {
  return localStorage.getItem(CacheKey.ACTIVE_THEME_NAME) as ThemeName | null
}
export const setActiveThemeName = (themeName: ThemeName) => {
  localStorage.setItem(CacheKey.ACTIVE_THEME_NAME, themeName)
}
//#endregion

//#region 标签栏
export const getVisitedViews = () => {
  const json = localStorage.getItem(CacheKey.VISITED_VIEWS)
  return JSON.parse(json ?? "[]") as TagView[]
}
export const setVisitedViews = (views: TagView[]) => {
  views.forEach((view) => {
    // 删除不必要的属性,防止 JSON.stringify 处理到循环引用
    delete view.matched
    delete view.redirectedFrom
  })
  localStorage.setItem(CacheKey.VISITED_VIEWS, JSON.stringify(views))
}
export const getCachedViews = () => {
  const json = localStorage.getItem(CacheKey.CACHED_VIEWS)
  return JSON.parse(json ?? "[]") as string[]
}
export const setCachedViews = (views: string[]) => {
  localStorage.setItem(CacheKey.CACHED_VIEWS, JSON.stringify(views))
}
//#endregion

key

const SYSTEM_NAME = "v3-admin-vite"

/** 缓存数据时用到的 Key */
class CacheKey {
  static readonly TOKEN = `${SYSTEM_NAME}-token-key`
  static readonly CONFIG_LAYOUT = `${SYSTEM_NAME}-config-layout-key`
  static readonly SIDEBAR_STATUS = `${SYSTEM_NAME}-sidebar-status-key`
  static readonly ACTIVE_THEME_NAME = `${SYSTEM_NAME}-active-theme-name-key`
  static readonly VISITED_VIEWS = `${SYSTEM_NAME}-visited-views-key`
  static readonly CACHED_VIEWS = `${SYSTEM_NAME}-cached-views-key`
}

export default CacheKey

里面有遇到一些以前没用过的

比如watchEffect函数,为vue自带的监听函数,在页面最开始的时候用初始化执行了该监听函数,然后在后边数据变化的时候自动变化主题色~

自动依赖收集:当你调用 watchEffect 时,Vue 会开始追踪你在副作用函数中访问的所有响应式数据。一旦这些数据发生变化,副作用函数会自动重新执行。

立即执行:与 watch 不同,watchEffect 在创建时会立即执行一次副作用函数,以捕获初始状态。

响应式清理:如果副作用函数返回一个函数,那么这个返回的函数会在组件卸载或重新渲染时作为清理函数被调用。这可以用于清理副作用,如取消定时器或解绑事件监听器。

停止观察:watchEffect 返回一个停止观察的函数,你可以调用这个函数来停止副作用的执行和依赖的收集。
                        
watchEffect 讲解链接:watchEffect的使用_watcheffect有什么用-CSDN博客

标签:CacheKey,const,NAME,主题,value,export,切换,Vue3,localStorage
From: https://blog.csdn.net/qq_52368602/article/details/139494564

相关文章

  • 【第5章】SpringBoot实战篇之登录模式切换
    文章目录前言一、接口扩展1.LoginStorage2.LocalLoginStorage3.RedisLoginStorage4.参数配置二、登录相关接口改动1.登录接口2.登录拦截器总结前言前面分别介绍了本地Map和redis存储用户登录信息,但是第二天我登录就出现问题了,因为我Redis部署在虚拟机里面,不......
  • Sz-Admin | SpringBoot3 JDK21 Vue3开源后台RBAC管理系统 | 2024年好用的开源RBAC管理
    简介接触了很多优秀的开源和闭源项目,在使用过程中也发现一些问题,不甘满足的我遂产生了想法:于是利用休息时间编写了一套后台管理系统,它灵活、简洁、高效,拥抱最新的技术,因此Sz-Admin便诞生了,也意为升职Admin,升职加薪节节高。SzAdmin,一个基于SpringBoot3、Vue3和El......
  • vue3+vueCli实现自动引入 unplugin-auto-import插件版本问题
    vue3项目引入unplugin-auto-import后报错通过引入的方式constAutoImport=require('unplugin-auto-import/webpack');报错如下: 通过直接官网vue-cli方式直接引入 报错如下经测试,是unplugin-auto-import插件版本问题查看unplugin-auto-import插件版本:npmlistu......
  • js实现点击切换图片,常用于图片画廊、产品展示、选项卡切换等
            以下代码实现产品展示或图片画廊相关的功能。具体来说,它实现了一个带有导航和对应内容区域的产品展示页面。用户通过导航来浏览不同的产品/项目,并在内容区域中查看与每个产品/项目相关的详细信息(在这种情况下是两张图片)。导航与内容的联动:页面有一个导航区域(......
  • provide inject vue3 父子组件 传参方式
    provideinjectvue3父子组件传参方式当子组件有30个的时候,这个就有优势了,在父组件provide一次,在子组件里面inject这个变量(实际上是通过hooks提供,也可以是个函数)。下面看下截图父组件:子组件:父组件provide子组件在父组件,就不用一堆props这里有一个特别的好处就是结构......
  • uniapp+vue3+swiper 高度自适应
    转自:https://blog.csdn.net/hjd2018/article/details/137261819  <template> <viewclass="top_swiper"> <swiper@change="onSwiperChange"class="swiper":style="{height:swiperHeight}"circularindicat......
  • 垃圾分类 环保主题 网页设计 html源码 大作业
    ......
  • 如何在Vue3中使用provide/inject实现跨组件状态共享?
    在前端开发中,组件之间的状态管理和数据共享是一个常见且重要的话题。Vue3作为一个流行的前端框架,提供了多种方法来解决这个问题。今天,我们将深入探讨在Vue3中使用provide和inject来实现跨组件状态共享的方法。什么是provide/inject?provide和inject是Vue3中提供的一种机制,......
  • 进程切换分析(1):基本框架
    一、前言本文主要是以context_switch为起点,分析了整个进程切换过程中的基本操作和基本的代码框架,很多细节,例如tlb的操作,cache的操作,锁的操作等等会在其他专门的文档中描述。进程切换包括体系结构相关的代码和系统结构无关的代码。第二、三、四分别描述了context_switch的代码脉络......
  • Zibll子比主题美化WordPress底部添加渐变色统计代码 新增显示评论总数及本周发布文章
    在网站底部添加一个统计信息,可以看到网站的运营情况,如会员数量,文章数量、网站的总浏览量、当天发布的文章数量、网站运营天数等,并且这个统计信息背景是渐变色的,非常漂亮。之前有分享过这个代码,今天新增了一个显示评论总数,把今天发布文章数量我改为了本周发布的数量,如果需要之前......