Vue UI框架的主题切换功能实现

 更新时间:2022年12月05日 08:52:56   作者:冻羊的小屋  
在如今,很多网页已经可以手动切换明亮模式和黑暗模式,网页的主题切换已经成为了一个常用的需求,因此,本文将从常见框架的处理方式总结一些相关的操作,对vue ui框架主题切换功能感兴趣的朋友跟随小编一起看看吧

在如今,很多网页已经可以手动切换明亮模式和黑暗模式,网页的主题切换已经成为了一个常用的需求,因此,本文将从常见框架的处理方式总结一些相关的操作,并探究其本质。

AntD 的方式

AntD 的 config-provider 似乎不能直接修改主题,目前网络上的教程有两类,一类是通过 less 的修改变量修改来完成,另一类是操作 DOM 改变对文件的引用,由于 ant-design-vue 已经提供了黑暗主题,而且直接修改 less 文件的变量涉及到了太多变量,不太方便,因此参考了这篇文章vue3点击出现弹窗后背景变暗且不可操作的实现代码,使用动态添加 CSS 引用 DOM 的方式实现黑暗主题的切换,原理是添加一个 DOM,引用我们自定义的 CSS 样式,后面的引用会覆盖前面的引用,在切换到亮色主题时把这个 DOM 删除。

1. 创建黑暗主题文件

在目录下的适当位置创建一个黑暗主题的文件,后面要引用这个文件,这里直接把官方黑暗主题复制过来就可以了,它的位置在 node_modules\ant-design-vue\dist\antd.dark.min.css,假设我们把它复制到了 public/styles/dark.css 中。

2. 创建切换主题的函数

有了主题文件,我们就可以创建切换主题的函数了,在目录中的合适位置创建一个 js 文件,例如 src/utils/theme.ts,其中的代码为:

const darkThemeSwitch = () => {
    // id要与前面一致
    const theme_element = document.querySelector('#dark-theme-style');
    if (!theme_element) {
        const new_dark_Theme = document.createElement('link')
        new_dark_Theme.setAttribute('rel', 'stylesheet');
        //刚刚设置的css路径
        new_dark_Theme.setAttribute('href', '/styles/dark.css');
        // id可以自由设置
        new_dark_Theme.setAttribute('id', 'dark-theme-style');
        const docHead = document.querySelector('head');
        docHead?.append(new_dark_Theme);
    } else {
        const parentNode = theme_element?.parentNode;
        parentNode?.removeChild(theme_element);
    }
}

export default darkThemeSwitch;

3. 切换主题

然后,我们在需要切换主题的地方使用这个方法就可以了:

<template>
    <a-button @click="toggleDark">切换主题</a-button>
</template>

<script setup lang="ts">
    import darkThemeSwitch from '../utils/theme';
    const toggleDark = () => {
        darkThemeSwitch();
    };
</script>

Quasar 的方式

quasar 的主题切换非常简单,只要使用 quasar 官方提供的 dark 插件就可以了,使用方法如下:

import { useQuasar } from 'quasar'
const $q = useQuasar()

// get status
console.log($q.dark.isActive) // true, false

// get configured status
console.log($q.dark.mode) // "auto", true, false

// set status
$q.dark.set(true) // or false or "auto"

// toggle
$q.dark.toggle()

通过研究 Quasar 的源码,可以发现,Quasar 的黑暗有两种设置方式,自动和手动,在自动模式下,插件通过监听 prefers-color-scheme: dark 的状态来决定当前网页的主题,通过这种方式,可以实现网页跟随系统主题自动变换浅色和深色模式,对应部分的代码如下:

Plugin.mode = val
if (val === 'auto') {
  if (Plugin.__media === void 0) {
    Plugin.__media = window.matchMedia('(prefers-color-scheme: dark)')
    Plugin.__updateMedia = () => { Plugin.set('auto') }
    Plugin.__media.addListener(Plugin.__updateMedia)
  }

  val = Plugin.__media.matches
}

上面的代码中,Plugin.mode 代表设置方式,Plugin.__media 的值默认就是 void 0,这里的判断的作用是当第一次执行时给相应的 Media 添加一个监听来获得实时的变化。这里调用的 Plugin.set('auto') 就是手动设置的函数,这个函数接受一个布尔值,然后根据布尔值的真假对主题进行设置,相应的实现如下:

document.body.classList.remove(`body--${ val === true ? 'light' : 'dark' }`)
document.body.classList.add(`body--${ val === true ? 'dark' : 'light' }`)

显然,这里的实现方式也比较常规,即在 body 标签添加或删除相应的样式选择器类名来实现主题的切换,quasar 值得我们学习的地方在于它把所有的功能集成到 useQuasar 中,用户可以方便地使用各种插件,而不需要写很多代码。完整的代码如下:

import defineReactivePlugin from '../utils/private/define-reactive-plugin.js'
import { isRuntimeSsrPreHydration } from './Platform.js'

const Plugin = defineReactivePlugin({
    // 插件的属性
    isActive: false,
    mode: false
}, {
    __media: void 0,
    // 插件的核心方法
    set(val) {
        if (__QUASAR_SSR_SERVER__) { return }

        Plugin.mode = val
        // 自动主题切换
        if (val === 'auto') {
            if (Plugin.__media === void 0) {
                Plugin.__media = window.matchMedia('(prefers-color-scheme: dark)')
                Plugin.__updateMedia = () => { Plugin.set('auto') }
                Plugin.__media.addListener(Plugin.__updateMedia)
            }

            val = Plugin.__media.matches
        }
        else if (Plugin.__media !== void 0) {
            Plugin.__media.removeListener(Plugin.__updateMedia)
            Plugin.__media = void 0
        }

        Plugin.isActive = val === true
        // 手动指定主题
        document.body.classList.remove(`body--${val === true ? 'light' : 'dark'}`)
        document.body.classList.add(`body--${val === true ? 'dark' : 'light'}`)
    },
    // 主题的切换
    toggle() {
        if (__QUASAR_SSR_SERVER__ !== true) {
            Plugin.set(Plugin.isActive === false)
        }
    },

    install({ $q, onSSRHydrated, ssrContext }) {
        const { dark } = $q.config

        if (__QUASAR_SSR_SERVER__) {
            this.isActive = dark === true

            $q.dark = {
                isActive: false,
                mode: false,
                set: val => {
                    ssrContext._meta.bodyClasses = ssrContext._meta.bodyClasses
                        .replace(' body--light', '')
                        .replace(' body--dark', '') + ` body--${val === true ? 'dark' : 'light'}`

                    $q.dark.isActive = val === true
                    $q.dark.mode = val
                },
                toggle: () => {
                    $q.dark.set($q.dark.isActive === false)
                }
            }

            $q.dark.set(dark)
            return
        }

        $q.dark = this

        if (this.__installed === true && dark === void 0) {
            return
        }

        this.isActive = dark === true

        const initialVal = dark !== void 0 ? dark : false

        if (isRuntimeSsrPreHydration.value === true) {
            const ssrSet = val => {
                this.__fromSSR = val
            }

            const originalSet = this.set

            this.set = ssrSet
            ssrSet(initialVal)

            onSSRHydrated.push(() => {
                this.set = originalSet
                this.set(this.__fromSSR)
            })
        }
        else {
            this.set(initialVal)
        }
    }
})

export default Plugin

ElementUI 的方式

在 2.2.0 版本,Element UI 也可以进行黑暗模式切换了,操作如下:

  • 在 main.ts 引入官方的 CSS:import 'element-plus/theme-chalk/dark/css-vars.css'

更改 html 标签的类,添加 dark 即为黑暗模式,去掉 dark 即为明亮模式,这一过程有很多实现方法,官方推荐使用 VueUse 这个函数库中的 useDark 来实现,这是一个非常强大的官方工具库,里面提供了许多常用功能的实现以及一些小插件,非常值得学习,请参考VueUse的官方文档:

<template>
    <el-button @click="toggleDark()">切换主题</el-button>
</template>

<script setup lang="ts">
    import { useDark, useToggle } from '@vueuse/core';

    const isDark = useDark();
    const toggleDark = useToggle(isDark);
</script>

NaiveUI 的方式

NaiveUI 的黑暗主题是通过其提供的 Config Provider 组件来实现的,通过设置其 theme 属性为 null 或者 darkTheme(需要从 naive-ui 导入),可以更改其内部组件的主题,配合 Global Style 组件,可以设置全局的主题:

<template>
  <n-config-provider :theme="theme">
    <n-card>
      <n-space>
        <n-button @click="theme = darkTheme">
          深色
        </n-button>
        <n-button @click="theme = null">
          浅色
        </n-button>
      </n-space>
    </n-card>
    <!-- 加上这个后可以切换全局主题 -->
    <n-global-style />
  </n-config-provider>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { darkTheme } from 'naive-ui'
import type { GlobalTheme } from 'naive-ui'

const theme = ref<GlobalTheme | null>(null)
</script>

还可以通过 useOsTheme 获取系统的主题作为组件的主题:

<template>
  <n-config-provider :theme="theme">
    <n-card> 当前操作系统的主题是 {{ osTheme }}。 </n-card>
  </n-config-provider>
</template>

<script lang="ts">
import { defineComponent, computed } from 'vue'
import { useOsTheme, darkTheme } from 'naive-ui'

export default defineComponent({
  setup () {
    const osThemeRef = useOsTheme()
    return {
      theme: computed(() => (osThemeRef.value === 'dark' ? darkTheme : null)),
      osTheme: osThemeRef
    }
  }
})
</script>

让我们来看一下它的实现,好吧,我水平实在是有限,真的看不懂,但是看起来真的很厉害。

到此这篇关于Vue UI框架的主题切换功能实现的文章就介绍到这了,更多相关vue主题切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue跨窗口通信之新窗口调用父窗口方法实例

    vue跨窗口通信之新窗口调用父窗口方法实例

    由于开发需要,我需要在登录成功请求成功后,调用父窗口的一个点击事件方法,这篇文章主要给大家介绍了关于vue跨窗口通信之新窗口调用父窗口的相关资料,需要的朋友可以参考下
    2023-01-01
  • 1分钟Vue实现右键菜单

    1分钟Vue实现右键菜单

    今天给大家分享的是,如何在最短的时候内实现右键菜单。高效实现需求,避免重复造轮子。感兴趣的可以了解一下
    2021-10-10
  • Vue3中使用i18n,this.$t报错问题及解决

    Vue3中使用i18n,this.$t报错问题及解决

    这篇文章主要介绍了Vue3中使用i18n,this.$t报错问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • 简单理解vue中el、template、replace元素

    简单理解vue中el、template、replace元素

    这篇文章主要帮助大家简单理解vue中el、template、replace元素,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • vue定义全局变量和全局方法的方法示例

    vue定义全局变量和全局方法的方法示例

    这篇文章主要介绍了vue定义全局变量和全局方法的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • Vue组件的实现原理详细分析

    Vue组件的实现原理详细分析

    在日常业务开发中我们会经常封装一些业务组件,下面这篇文章主要给大家介绍了关于Vue组件的实现原理,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • vue配置nprogress实现页面顶部进度条

    vue配置nprogress实现页面顶部进度条

    这篇文章主要为大家详细介绍了vue配置nprogress实现页面顶部进度条,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • Vue3 计算属性computed的实现原理

    Vue3 计算属性computed的实现原理

    这篇文章主要介绍了Vue3 计算属性computed的实现原理,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-08-08
  • 在vue中读取本地Json文件的方法

    在vue中读取本地Json文件的方法

    今天小编就为大家分享一篇在vue中读取本地Json文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09
  • vue3中cookie的详细使用过程

    vue3中cookie的详细使用过程

    近期做的项目比较杂,涉及到前端框架的工作,遇到了许多问题,记录一下这个比较棘手的问题,下面这篇文章主要给大家介绍了关于vue3中cookie的详细使用方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08

最新评论