发布于 2026-01-06 1 阅读
0

Vue + Tailwind 2.0:使用 Vuex、localStorage 和用户默认偏好设置实现暗黑模式

Vue + Tailwind 2.0:使用 Vuex、localStorage 和用户默认偏好设置实现暗黑模式

TailwindCSS 2.0太棒了!原生暗黑模式、海量颜色,还有其他一大堆新功能。我正好在 Tailwind 2.0 发布的时候启动了一个新的Gridsome项目,用来记录我最近开发的 tea 依赖项,所以我想加入一些主题切换功能,预示着即将到来的暗黑时代。

我想要的那种条件主题有点复杂:

  • 首次访问者应看到他们首选操作系统/浏览器的主题。
  • 用户选择的主题应在其整个会话期间得到尊重。
  • 用户选择的主题应该保存在本地存储中,这样他们返回时就无需费力地在用户界面上进行设置。

虽然 Tailwind 提供了手动选择主题的选项,但文档中关于手动驾驶的示例却含糊不清,故意没有具体说明。这CTRL+C CTRL+V在这里帮不上什么忙,所以我们只能在 VS Code 中自行摸索了。

让我们使用VuexlocalStorage来实现我们自己的有状态暗黑模式解决方案。

深层政府

在尝试将 Vuex 添加到现有的 Gridsome 项目中 45 分钟后(期间 Gridsome 官方文档似乎要把我送上刑场)console.error,我最终成功地创建了自己的 Vuex 商店,并为此感到自豪:

import Vue from 'vue'
import Vuex from 'vuex'
import theme from './modules/theme'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {},
    mutations: {},
    actions: {},
    modules: {
        theme
    },
})
Enter fullscreen mode Exit fullscreen mode

现在,在这个theme.js模块内部,我们可以使用一些简单的技巧,将大部分逻辑从前端隐藏起来。让我们从状态和变更开始:

export default {
    state: {
        theme: {}
    },
    mutations: {
        SET_THEME(state, theme) {
            state.theme = theme;
            localStorage.theme = theme;
        }
    },
...
Enter fullscreen mode Exit fullscreen mode

我们尽量简化了 mutation 操作,使其仅负责更新主题状态。将状态保存到 localStorage 中,可以让我们在用户关闭页面后再次访问时,能够检索到用户最近选择的主题,其作用类似于 cookie。

现在想想当一个新用户访问我们的网站时会发生什么。他们有可能在操作系统或浏览器中选择了浅色或深色主题,我们可以也应该尊重他们的选择。请注意,我们还没有初始化主题状态。这将是我们的第一个操作:

...
 actions: {
        initTheme({ commit }) {

            const cachedTheme = localStorage.theme ? localStorage.theme : false;
            //  `true` if the user has set theme to `dark` on browser/OS
            const userPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

            if (cachedTheme)
                commit('SET_THEME', cachedTheme)
            else if (userPrefersDark)
                commit('SET_THEME', 'dark')
            else
                commit('SET_THEME', 'light')

        },
...
Enter fullscreen mode Exit fullscreen mode

此操作将检查用户之前是否访问过该网站:

  • 如果是这样,我们就使用他们缓存的主题偏好。
  • 如果用户是新用户,我们会检查他们的系统是否设置为深色模式。如果是,我们会将我们的存储和缓存也设置为深色模式。
  • 否则,我们将默认使用浅色模式。

我们可以从应用程序无处不在的根目录来执行此操作,无论用户从哪个入口进入,最终都会访问到该目录:

// for me this is `layouts/Default.vue`, but for you it may be `App.vue` or something else
export default {
  beforeMount() {
    this.$store.dispatch("initTheme");
  },
...
Enter fullscreen mode Exit fullscreen mode

现在我们需要一个开关来切换主题。

在创建该组件之前,让我们先回去theme.js添加逻辑:

...
// This simply flips whatever was most recently committed to storage.
    toggleTheme({ commit }) {

            switch (localStorage.theme) {
                case 'light':
                    commit('SET_THEME', 'dark')
                    break;

                default:
                    commit('SET_THEME', 'light')
                    break;
            }
        }
    },
    getters: {
        getTheme: (state) => {
            return state.theme;
        }
    },
}
Enter fullscreen mode Exit fullscreen mode

Tailwind 2.0 的dark课程

接下来,我们可以添加一些属性,以便在深色模式下有条件地渲染。然后,我们将设置一个监视器,它会对主题选择的更改做出反应,运行一个函数,该函数会将 Tailwind 的darkCSS 类添加到我们应用程序的根节点,或从中移除类:

<template>
  <main
    class="min-h-screen 
           bg-green-50 text-gray-700 
           dark:bg-gray-900 dark:text-purple-50">
    <ThemeToggler/>
    <slot />
  </main>
</template>

<script>
import { mapGetters } from "vuex";
import ThemeToggler from "../components/ThemeToggler.vue";

export default {
  components: {
    ThemeToggler,
  },
  beforeMount() {
    this.$store.dispatch("initTheme");
  },
  computed: {
    ...mapGetters({ theme: "getTheme" }),
  },
  watch: {
    theme(newTheme, oldTheme) {
      newTheme === "light"
        ? document.querySelector("html").classList.remove("dark")
        : document.querySelector("html").classList.add("dark");
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

最后,我们还要介绍最后一个部件,我们那令人极其失望的切换器:

<template>
    <button 
     @click="toggleTheme"
     class="dark:text-red-400 text-cyan-200">
      Theme Toggle
    </button>
</template>

<script>
export default {
  methods: {
    toggleTheme() {
      this.$store.dispatch("toggleTheme");
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

创作自由

这很棒,因为你可以尽情发挥创意,用 SVG 替换文本,并添加各种各样的过渡效果来实现状态转换。你可以使用 Vue 内置的过渡元素或 Tailwind 的过渡类,或者两者都用!

如果您有任何问题、建议或更优雅的解决方案,请在下方留言!我发现有几个地方可以做得更优雅,例如使用 Vue 的条件类而不是直接给dark根元素添加或移除类。

文章来源:https://dev.to/anthonygushu/vue-tailwind-2-0-dark-mode-using-vuex-localstorage-and-user-s-default-preference-439g