如何在Vue.js项目中正确实现简易明暗主题切换器
关于Vue项目中实现Tailwind深色/浅色主题切换的解答
能不能直接把教程里的vanilla JS代码放到main.js运行?
短答案:能跑,但非常不建议这么做。
硬把原生JS直接操作DOM的逻辑塞到Vue入口文件里,虽然大概率能实现基础的切换效果,但会有不少实际问题:
- 代码要等Vue应用挂载完成才会执行,首屏加载时会出现明显的主题闪烁——比如用户存了深色主题偏好,页面会先渲染成浅色,等JS跑完才切成深色,体验很差
- 主题状态完全脱离Vue响应式体系,其他组件如果需要根据当前主题做联动(比如切换图表配色、换主题对应logo),只能手动读DOM类名或者localStorage,没法自动响应更新
- 全局DOM操作逻辑散落在入口文件,不符合Vue的组件化封装规范,后续维护、排查问题成本很高,甚至可能和虚拟DOM的更新逻辑冲突产生偶发bug
- 如果项目用了SSR(比如Nuxt、Vite SSR),直接在
main.js里访问window/document对象还会触发服务端渲染报错。
符合Vue规范的实现方案
整个实现对齐Tailwind深色模式的官方推荐逻辑,同时适配Vue的响应式特性,还能解决首屏闪屏问题,步骤如下:
- 配置Tailwind深色模式规则
先在Tailwind配置文件里开启基于class的深色模式切换,这是所有逻辑的基础:/** @type {import('tailwindcss').Config} */ export default { darkMode: 'class', // 保留项目原有的content、theme等其他配置 } - 添加首屏主题初始化脚本(解决闪屏核心)
把主题初始化的逻辑放到index.html的<head>标签最靠前位置,这段代码会在页面资源加载、Vue挂载之前就执行,直接给根节点设置对应主题类,完全避免闪屏:<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <!-- 初始化脚本放在所有资源引用之前 --> <script> const savedTheme = localStorage.getItem('theme') const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches if (savedTheme === 'dark' || (!savedTheme && systemPrefersDark)) { document.documentElement.classList.add('dark') } else { document.documentElement.classList.remove('dark') } </script> <!-- 后面是原有的meta、样式、脚本引用 --> </head> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script> </body> </html> - 封装可复用的主题逻辑Composable
把主题状态、切换方法封装成通用组合式函数,全项目任意组件都可以调用,状态全局共享:// src/composables/useTheme.js import { ref, watchEffect } from 'vue' // 初始化时优先读本地存储,没有就读系统主题偏好 const currentTheme = ref( localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') ) // 自动同步状态到DOM和本地存储 watchEffect(() => { localStorage.setItem('theme', currentTheme.value) currentTheme.value === 'dark' ? document.documentElement.classList.add('dark') : document.documentElement.classList.remove('dark') }) export function useTheme() { const toggleTheme = () => { currentTheme.value = currentTheme.value === 'dark' ? 'light' : 'dark' } return { theme: currentTheme, toggleTheme } } - 在组件中使用
直接写个切换按钮组件,任意页面引入就能用,主题状态是响应式的:<!-- src/components/ThemeToggle.vue --> <script setup> import { useTheme } from '@/composables/useTheme' const { theme, toggleTheme } = useTheme() </script> <template> <button @click="toggleTheme" class="p-2 rounded-md bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-800 dark:text-gray-100 transition-colors" > <span v-if="theme === 'dark'">切到浅色模式</span> <span v-else>切到深色模式</span> </button> </template>
如果是中大型项目,也可以把主题状态放到Pinia里管理,逻辑和上面的composable基本一致,只是把状态存在store里方便搭配其他全局状态做联动。
内容的提问来源于stack exchange,提问作者raresample
相关产品推荐
相关产品推荐

