vue3 中 主题定制

vue3 中 主题定制

背景

做多主题定制,黑/白 ,里面还要再分各种颜色,每次进来都要记住上次的主题设置

效果图

一、目录结构

复制代码
├── generated
│   ├── theme  
│   │   └── dark-yellow.ts
│   │   └── dark-orange.ts
│   │   └── light-yellow.ts
│   │   └── light-orange.ts
│   │   └── theme.enums.ts
├── stores 
│   ├── theme-store.ts  

数据结构

主题

枚举

二、流程

  1. Init的时候,根据 store 中的颜色主题,先做一次匹配,changeTheme
  2. 切换主题的时候,监听绑定的值,做 changeTheme
  3. changeTheme 主要就是读取 文件列表,做匹配,做规则制定,最后使用 document.documentElement.style.setProperty 设置style,根据css 变量做匹配

三、核心实现

  1. init 时,设置已存储的theme
js 复制代码
import { setupTheme } from '@/stores/theme-store'
const app = createApp(App)
initApp(app)


const initApp = async (app: App) => {
  setupStore(app)
  setupRouter(app)
  setupLang(app)
  setupTheme()
  app.mount('#app')
}
  1. 读取配置主题文件(匹配到的文件默认是懒加载的,通过动态导入实现eager: true)
js 复制代码
const themeFileList: Record<string, string> = import.meta.glob(
  ['@/generated/theme/*.ts', '!@/generated/theme/theme.enums.ts'],
  {
    import: 'default',
    eager: true
  }
)
  1. 根据文件夹匹配出主题key
js 复制代码
const themeFileListObject = {}
for (const key in themeFileList) {
  const filename = key.split('/').pop()?.replace('.ts', '')
  if (filename) {
    themeFileListObject[filename] = themeFileList[key]
  }
}
  1. 替换root 样式(根据存储key匹配出当前色值表,format css name,then set style)
js 复制代码
const injectRootStyle = (theme: ThemeEnum) => {
  const themeObject = themeFileListObject[theme]
  for (const key in themeObject) {
    const cssVariableName = `--${key.replace(/_/g, '-').toLowerCase()}`
    document.documentElement.style.setProperty(cssVariableName, themeObject[key])
  }
}
  1. Pian 中做数据监听,data change to call changeTheme function.
js 复制代码
watch(theme, (nVal) => {
      changeTheme(nVal)
})
// 改变主题
const changeTheme = (theme: ThemeEnum) => {
  injectRootStyle(theme)
}
//设置主题
const setupTheme = () => {
  changeTheme(useThemeStore().theme)
}

四、总体代码

js 复制代码
import ThemeEnum from '@/generated/theme/theme-enum'
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'

// 读取全部主题配置
const themeFileList: Record<string, string> = import.meta.glob(
  ['@/generated/theme/*.ts', '!@/generated/theme/theme.enums.ts'],
  {
    import: 'default',
    eager: true
  }
)

// 根据读取的文件路径,生成名称和地址
const themeFileListObject = {}
for (const key in themeFileList) {
  const filename = key.split('/').pop()?.replace('.ts', '')
  if (filename) {
    themeFileListObject[filename] = themeFileList[key]
  }
}

// 注入根样式
const injectRootStyle = (theme: ThemeEnum) => {
  const themeObject = themeFileListObject[theme]
  for (const key in themeObject) {
    const cssVariableName = `--${key.replace(/_/g, '-').toLowerCase()}`
    document.documentElement.style.setProperty(cssVariableName, themeObject[key])
  }
}

// 定义一个函数,用于改变主题
const changeTheme = (theme: ThemeEnum) => {
  // 注入根样式
  injectRootStyle(theme)
}

// 定义一个函数,用于设置主题
const setupTheme = () => {
  // 改变主题
  changeTheme(useThemeStore().theme)
}

const useThemeStore = defineStore(
  'theme',
  () => {
    const theme = ref(ThemeEnum['light-red'])

    watch(theme, (nVal) => {
      changeTheme(nVal)
    })

    return { theme }
  },
  { persist: true }
)

export { setupTheme, changeTheme, useThemeStore }

五、总结

  1. 我们在切换主题的时候,在组件内其实没有做任何处理,是在pinia 里做的监听
  2. 持久化 这里用到了 persist 插件
  3. 通过向外暴露setupTheme 来实现 修改主题
相关推荐
sibylyue3 分钟前
# Web端流媒体JS播放器开源库
前端·javascript·开源
码视野15 分钟前
基于 Vue3 + Element Plus 的【基于物联网与深度学习的智慧工业园区能耗监测与光储充一体化微电网协同调度系统】设计与实现(附完整源码与PRD)
前端·人工智能·深度学习·物联网·vue3
铁皮饭盒18 分钟前
网页端, 5.5mb谷歌模型, 识别躯干, 视频都不卡
前端·javascript·后端
雪芽蓝域zzs34 分钟前
Prettier (代码格式化)配置详解
前端·vue.js
晴天1634 分钟前
Gradio 与 Streamlit:Python 快速构建 Web 应用的双子星-Day28
开发语言·前端·python
码哥DFS40 分钟前
LRU缓存
前端·javascript·缓存
mldong41 分钟前
一条命令,十分钟:jeeflow 工作流应用的六语言一键部署
前端·后端·架构
zzzzzz3102 小时前
别急着把页面做成舞台:从 react-bits 看动画组件该怎么选
javascript·react.js·开源
运维全栈笔记4 小时前
Vue + Spring Boot 前后端分离项目部署笔记(若依 RuoYi-Vue 3.9.2)
运维·服务器·vue.js·spring boot·笔记·开源·开源软件
ly76899 小时前
HTML5 从入门到工程实践:语义化、表单、多媒体、性能与项目规范详解
前端·html·html5