Vue3主题切换

news2024/12/22 23:04:02

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

网上有很多主题切换的案例,但是别人的终究是别人的,研究透彻你才能灵活运用。这边用的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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1796684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

gitee上传整个项目文件夹

1.访问git官网并下载 Git 如下图&#xff1a; 点击download&#xff0c;然后选择合适的版本进行下载&#xff1a; 如下图&#xff0c;我下载的是2.32.0.2版本&#xff0c;64位windows版。 下载完之后&#xff0c;直接点击安装。 然后根据向导&#xff0c;一路默认到安装完成。…

超越预期:Containerd 如何成为 Kubernetes 的首选容器运行时

> 作者&#xff1a;尹珉&#xff0c;KubeSphere Ambassado&#xff0c;rKubeSphere Contributor&#xff0c;KubeSphere 社区用户委员会杭州站站长。 踏上 Containerd 技术之旅 容器技术已经成为现代软件开发和部署的核心工具。通过容器&#xff0c;开发者可以创建轻量级…

Unity【入门】重要组件和API

Unity重要组件和API 文章目录 1、最小单位GameObject1、成员变量2、静态方法1、代码创建Unity自带几何体 CreatePrimitive2、查找对象3、实例化对象&#xff08;克隆对象&#xff09;的方法4、删除对象的方法5、切换场景不移除 3、成员方法1、创建空物体2、为对象动态添加脚本(…

ESXi内安装OpenWrt

目录 0、前言 1、环境 2、转换格式 3、创建虚拟机 4、OpenWrt设置 5、单臂流量测试 6、总结 0、前言 前几天在ESXi中先安装了PVE,然后在PVE中安装OpenWrt,没有来得及深入测试,仅仅作为安装和熟悉PVE的过程。后来转念一想为什么不在ES…

OpenStack云平台管理

OpenStack云平台管理 文章目录 OpenStack云平台管理资源列表基础环境一、部署Openstack二、创建网络和路由2.1、删除默认的网络2.2、创建网络和路由2.2.1、创建外部网络2.2.2、创建内部网络 2.3、创建路由 三、创建实例3.1、配置实例3.2、配置NAT转换 四、绑定浮动IP地址五、添…

Nacos的下载使用

下载 本地Java8Maven环境 下载地址 https://github.com/alibaba/nacos/releases使用 解压安装包&#xff0c;直接运行bin目录下的startup.cmd 账号密码都是nacos

iphone突然黑屏?3种方法解决问题!

iphone突然黑屏这应该是出现了iOS系统故障。一般来说&#xff0c;无摔落、浸水等情况造成设备出现硬件问题&#xff0c;多半是设备出现了系统故障。 虽然苹果iOS系统性能是较优的&#xff0c;但还是可能因各种使用情况出现不一样的iOS系统问题&#xff0c;包括升级、越狱、安装…

Python疑难杂症--考试复习

1.排序输出字典中数据 dic1 {Tom:21,Bob:18,Jack:23,Ana:20} dic2 {李雷:21,韩梅梅:18,小明:23,小红:20} nint(input()) if n>len(dic1):nlen(dic1) print(sorted(dic1.keys())[:n]) print(sorted(dic2.items(),keylambda item:item[1])[:n]) 2.罗马数字转换 def F(s):d{…

GPT-4o如何重塑AI未来!

如何评价GPT-4o? 简介&#xff1a;最近&#xff0c;GPT-4o横空出世。对GPT-4o这一人工智能技术进行评价&#xff0c;包括版本间的对比分析、GPT-4o的技术能力以及个人感受等。 GPT-4o似乎是一个针对GPT-4模型进行优化的版本&#xff0c;它在性能、准确性、资源效率以及安全和…

Ubuntu22.04之安装emacs29.3版本(二百四十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

vulnhub靶机实战_DC-2

下载 靶机下载链接汇总&#xff1a;https://download.vulnhub.com/使用搜索功能&#xff0c;搜索dc类型的靶机即可。本次实战使用的靶机是&#xff1a;DC-2下载链接&#xff1a;https://download.vulnhub.com/dc/DC-2.zip 启动 下载完成后&#xff0c;打开VMware软件&#xf…

docker安装rabbitmq详解

目录 1、安装 1-1.查看rabbitmq镜像 1-2.下载Rabbitmq的镜像 1-3.创建并运行rabbitmq容器 1-4.查看启动情况 1-5.启动web客户端 1-6.访问rabbitmq的客户端 2..遇到的问题 解决方法: 1、安装 1-1.查看rabbitmq镜像 docker search rabbitmq 1-2.下载Rabbitmq的镜像 拉…

UE4_Ben_图形52_水下效果处理

学习笔记&#xff0c;不喜勿喷&#xff0c;欢迎指正&#xff0c;侵权立删&#xff01;祝愿生活越来越好&#xff01; 在这个后期处理的效果中&#xff0c;我们可以看到有很多不同的&#xff0c;这里有浓雾&#xff0c;波纹扭曲&#xff0c;镜头扭曲和边缘模糊&#xff0c;在第4…

实验五、IPv4地址的子网划分,第1部分《计算机网络》

但凡你有点本事&#xff0c;也不至于一点本事都没有。 目录 一、实验目的 二、实验内容 三、实验小结 一、实验目的 完成本练习之后&#xff0c;您应该能够确定给定 IP 地址和网络掩码 的网络信息。本练习旨在让您掌握如何根据给定 IP 地址计算网络 IP 地址信息。 二、实验…

C# E2Pose人体关键点检测(OpenVINO推理)

C# E2Pose人体关键点检测(OpenVINO推理) 目录 效果 模型信息 项目 代码 下载 效果 模型信息 Inputs ------------------------- name&#xff1a;inputimg tensor&#xff1a;Float[1, 3, 512, 512] --------------------------------------------------------------- Ou…

构建自动化API数据抓取系统

构建一个自动化API数据抓取系统是一个涉及多个技术领域的复杂任务。这样的系统不仅要求高效的数据获取能力&#xff0c;还需要有稳定的数据处理、存储和错误处理机制。 1. 需求分析 在开始构建之前&#xff0c;明确你的需求至关重要。你需要确定要抓取的API、数据的频率、数据的…

Jenkins工作流程原理

持续集成&#xff1a;自动部署打包发布代码 Jenkins工作流程 项目已经基于Jenkins实现了持续集成&#xff0c;每当我们push代码时&#xff0c;就会触发项目完成自动编译和打包。而需要运行某个微服务时&#xff0c;我们只需要经过两步&#xff1a; 第一步&#xff0c;访问je…

CS1061 “HtmlHelper”未包含“Partial”的定义,并且找不到可接受第一个“HtmlHelper”类型参数的可访问扩展方法“Partial”

严重性 代码 说明 项目 文件 行 禁止显示状态 错误 CS1061 “HtmlHelper”未包含“Partial”的定义&#xff0c;并且找不到可接受第一个“HtmlHelper”类型参数的可访问扩展方法“Partial”(是否缺少 using 指令或程序集引用?) 14_Views_Message_E…

【刷题篇】分治-快速排序

文章目录 1、颜色分类2、 排序数组3、数组中的第K个最大元素4、库存管理 III 1、颜色分类 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 我们使用整…

前端开发环境:Vue、Element Plus、Axios

目录 1. Vue简介 2. Element Plus简介 3. Axios简介 4. 创建Vue项目 4.1 Node.js安装 4.2 创建Vue项目 4.3 Vue项目的结构 4.4 安装Element-Plus 4.5 安装Axios 4.6 解决跨域问题 5. 应用实例 5.1 创建Vue组件 5.2 配置路由 5.3 配置根组件 5.4 启动前端应用服…