vue implements multi-window theme style switching and following system switching

vue implements multi-window theme style switching and following system switching

自我记录

1. Link to knowledge points: https://developer.mozilla.org/zh-CN/docs/Web/CSS/@media/prefers-color-scheme
1. Link to knowledge points: https://www.runoob.com/ jsref/met-win-matchmedia.html
2. Monitor storage changes (cross-tab communication)
3. watchEffect listener

1.js+css to match the system theme color

// 使用matchMedia API 去匹配css的媒体查询的 系统的主题是否为案(dark)颜色 (也可以去匹配屏幕的宽度高度等)
const match = matchMedia('(prefers-color-scheme:dark)')
console.log(match,'match');

Insert image description here

2. Cross-label communication

window.addEventListener('storage', (event) => {
    
    
      console.log(event.newValue)
    })

When the storage of page A changes, page B can monitor it.
Insert image description here

3. Monitor local storage changes and assign values

watchEffect(() => {
    
    
 localStorage.setItem(LOCAL_KEY, theme.value)
 document.documentElement.className = theme.value
})

4. All codes

<template>
  <a-layout-header class="head-box" :style="{ position: 'fixed', zIndex: 1, width: '100%' }">
    <a-row :gutter="{ xs: 24, sm: 24, md: 24, lg: 24 }">
      <a-col :xs="7" :sm="7" :md="7" :lg="7">奔跑的代码!</a-col>
      <a-col :xs="6" :sm="6" :md="6" :lg="6">奔跑的代码!</a-col>
      <a-col :xs="6" :sm="6" :md="6" :lg="6">奔跑的代码!</a-col>
      <a-col :xs="5" :sm="5" :md="5" :lg="5">
        <!-- 开关切换主题 -->
        <a-switch v-model:checked="checked" checked-children="经典" un-checked-children="深夜" @change="onChange" />
      </a-col>
    </a-row>
  </a-layout-header>
</template>
<script setup>
import {
    
     ref, watchEffect } from 'vue'

const checked = ref(false)
// 切换主题样式
// 'light' | 'dark'
const LOCAL_KEY = '__theme__'
const theme = ref(localStorage.getItem(LOCAL_KEY) || 'light')
// 使用matchMedia API 去匹配css的媒体查询的 系统的主题是否为案(dark)颜色 (也可以去匹配屏幕的宽度高度等)
const match = matchMedia('(prefers-color-scheme:dark)')
console.log(match,'match');
// 跟随系统
const followOs = () => {
    
    
  // 如果当前系统时暗色 
  if (match.matches) {
    
    
  	// 本地存储重新赋值
    localStorage.setItem(LOCAL_KEY, 'dark')
    // html 的class 重新赋值
    document.documentElement.className = 'dark'
    // 优化了 页面滑块按钮的赋值
    checked.value = true
  } else {
    
    
    localStorage.setItem(LOCAL_KEY, 'light')
    document.documentElement.className = 'light'
    checked.value = false
  }
}
// matchMedia 的API 当 matchMedia('(prefers-color-scheme:dark)') 查询结果有变化时
// 监听主题的变化 
match.addEventListener('change', followOs)

watchEffect(() => {
    
    
  // 仅需要多个标签页联动 用这俩  
  // localStorage.setItem(LOCAL_KEY, theme.value)
  // document.documentElement.className = theme.value
  // 使用跟随系统功能 用这个函数
  followOs()
})
// 这个是antd滑块的事件 
const onChange = (value) => {
    
    
  const temp = value ? 'dark' : 'light'
  if (value) {
    
    
    theme.value = temp
    localStorage.setItem(LOCAL_KEY, temp)
    document.documentElement.className = temp
  } else {
    
    
    theme.value = temp
    localStorage.setItem(LOCAL_KEY, temp)
    document.documentElement.className = temp
  }


};
// 监听本地存储的变化 为了实现多个标签页联动
addEventListener('storage', event => {
    
    
  console.log(event, 'ev');
  document.documentElement.className = event.newValue
  checked.value = event.newValue === 'light' ? false : true
})

</script>
<style lang="less" scoped>
.head-box {
    
    
  width: 100%;
  height: 64px;
  background: @bgc-head-color;
  padding: 0 20px;
}
</style>

element Plus theme switching

Final display effect

The code APIs used to implement the functions are all above. You can also implement the desired functions according to your own preferences.
At first, I only implemented a theme switching function. Yesterday, a friend suddenly told me that her fairy product manager wanted one as many as Element Plus. The linkage theme switching function of each tab page.
Next, there is the above code. If it helps you, please like and collect it to support it!

Below is my record of simple configuration of theme switching of vite+vue3+antd

Insert image description here
Insert image description here
Insert image description here
vite.config.js

import {
    
    defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue' //是vite 对vue 对支持插件
import Components from 'unplugin-vue-components/vite'
import path from 'path'
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor";
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
import {
    
     AntDesignVueResolver } from 'unplugin-vue-components/resolvers'

// https://vitejs.dev/config/
export default defineConfig(({
     
      mode }) => {
    
    
    return {
    
    
        plugins: [
            vue(),
            VueSetupExtend(),
            Components({
    
    
                resolvers: [
                    AntDesignVueResolver({
    
    
                        importStyle: 'less', // 一定要开启这个配置项
                    }),
                ],
            }),
            themePreprocessorPlugin({
    
    
                less: {
    
    
                    // 各个主题文件的位置
                    multipleScopeVars: [
                        {
    
    
                            scopeName: "light",
                            path: path.resolve("src/assets/style/light.less"),
                        },
                        {
    
    
                            scopeName: "dark",
                            path: path.resolve("src/assets/style/dark.less"),
                        },
                    ],
                },
            }),
        ],
        base: "./",//公共基础路径 打包路径
        resolve: {
    
    
            alias: {
    
    
                '@': path.resolve(__dirname, './src')//别名设置
            },
            extensions: ['.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
        },
        server: {
    
    
            // host: 'localhost',
            port: 8081,//启动端口
            // https: true, // 开启https
            open: true, // 自动开启窗口
            proxy: {
    
     // 代理配置
                '/api': {
    
    
                    target: 'xxx',//后端服务地址
                    changeOrigin: true,
                    rewrite: (path) => path.replace(/^\/api/, '') // 重写路径
                }
            },
        },
        css: {
    
    
            preprocessorOptions: {
    
    
                less: {
    
    
                    // modifyVars:
                    //     {
    
    
                    //         'primary-color': '#1DA57A',
                    //         'link-color': '#1DA57A',
                    //         'border-radius-base': '2px',
                    // },
                    additionalData: `@import './src/assets/style/index.less';`,
                    javascriptEnabled: true,
                },
                stylus: {
    
    
                    /*vite  根据官档  @import 引入stylus不生效 需要通过绝对路径导入 */
                    // imports: [path.resolve(__dirname, 'src/stylus/...styl')]  //配置全局变量
                }
            },
        },
    }
})

It is not easy to summarize, but I hope it will be helpful to you. I hope we can learn and make progress together.

Guess you like

Origin blog.csdn.net/zhgweb/article/details/131004125