Featured image of post Nuxt3 PrimeVue 暗黑模式切换

Nuxt3 PrimeVue 暗黑模式切换

交互方案

前一篇文章,记录了从零开始搭建一个 Nuxt + PrimeVue + TailwindCss 的最小化项目。今天继续给它增加一个非常酷的功能:暗黑模式切换

一般来说,暗黑模式的切换,有四个方案:

  1. 方案一:三个选项,浅色、暗黑、跟随系统,用下拉菜单选择
  • 优点:提供更多的选择,用户可以根据自己的需求选择跟随系统设置。
  • 缺点:增加了操作复杂度,用户需要在三个选项中进行选择。至少要点击 2 次。
  1. 方案二,二个选项,浅色、暗黑,用一个图标点击切换
  • 优点:简化了操作,用户只需在浅色和深色之间切换。
  • 缺点:失去了跟随系统设置的功能。
  1. 方案三:三个选项,平排放三个图标供用户点击
  • 优点:简化操作,只需要点击一下
  • 缺点:并排放三个图标,尤其是在工具栏这种地方,比较难看,且容易暄宾夺主,
  1. 方案四:三个选项,只放一个图标,点击轮换
  • 优点:操作简化,配合图标和文字,也不用做多余的提示
  • 缺点:要切换回第一个值,要点两下

参考 Nuxt、PrimeVue 都只是提供了一个图标点击的方案,也就是两个选项,要么浅色,要么深色。我觉得两个选项也够用,用户点击一次就可以。 但是开发调试的时候就发现还是方案四比较好玩儿。所以我选方案四,一个图标,三个选项轮换。

那么如何保持用户的选择呢?仍然是少不了使用 cookie 或 localStorage 来记录。这里就要用到一个 Nuxt 的组件 @nuxtjs/color-mode。它能配置存在哪里,选择器的前缀、后缀。还能根据系统主题的切换自动来切换。

实现暗黑模式切换

就像 Nuxt 网站一样,先要有个图标,然后点击后切换深/浅色,同时图标也变成对应的另一个图标。这时候需要安装一个 PrimeVue 的图标库。当然,也有其它的很多图标库可以选,这里我先用 primeicons

1
2
npm install primeicons
npx nuxi module add color-mode

然后需要简单配置一下 nuxt.config.ts 在 modules 同级增加图标库的 css 引入,以及 colorMode的配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// https://nuxt.com/docs/api/configuration/nuxt-config
import Aura from "@primevue/themes/aura";

export default defineNuxtConfig({
  compatibilityDate: "2024-11-01",
  devtools: { enabled: true },
  modules: ["@primevue/nuxt-module", "@nuxtjs/tailwindcss", "@nuxtjs/color-mode"],
  css: [
    'primeicons/primeicons.css',
  ],

  colorMode: {
    classSuffix: '',   // 类名后缀,默认值"-mode",如果不加前缀和后缀,类名为 'dark'|'light',此时 Nuxt DevTools 也会跟着切换颜色模式
    preference: 'light',     // 默认偏好:'system' | 'light' | 'dark'
    fallback: 'light',        // 回退模式
    dataValue: 'theme',       // HTML 数据属性名
    storage: 'localStorage',  // 存储方式:'localStorage' | 'cookie'
    storageKey: 'nuxt-color-mode', // 存储键名
  },

  primevue: {
    autoImport: true, // 自动导入 PrimeVue 组件
    options: {
      ripple: true, // 是否启用涟漪效果
      theme: {
        preset: Aura, // 预设的主题,没有主题不显示样式
        options: {
          darkModeSelector: '.dark', // 暗黑模式选择器,对应 colorMode.classPrefix + {light|dark} + colorMode.classSuffix
        }
      },
    },
  },
});

然后components/AppHeader.vuescript 部分增加代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const colorMode = useColorMode()

// 定义模式顺序:light -> dark -> system -> light
const modes = ['dark', 'light', 'system']

// 计算当前图标
const modeIcon = computed(() => {
  switch (colorMode.preference) {
    case 'dark':
      return 'pi pi-moon'
    case 'light':
      return 'pi pi-sun'
    default:
      return 'pi pi-desktop'
  }
})

// 切换模式
const toggleMode = () => {
  const currentIndex = modes.indexOf(colorMode.preference)
  const nextIndex = (currentIndex + 1) % modes.length
  colorMode.preference = modes[nextIndex]
}

然后在登录按钮前面增加图标选择的按钮

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<client-only>
  <Button
    @click="toggleMode"
    outlined
    class="flex items-center gap-2 p-button-text"
  >
    <span class="pi" :class="modeIcon"></span>
    <span v-if="colorMode.preference === 'light'">Light</span>
    <span v-if="colorMode.preference === 'dark'">Dark</span>
    <span v-if="colorMode.preference === 'system'">Auto</span>
  </Button>
</client-only>

然后点击页面中的图标就可以看到效果了。但是。为什么只有头部 header 部分变黑,其它地方没有变黑呢?这是因为 app.vue 中还在引入 <NuxtWelcome /> 组件。该组件有自己的控制方式。所以我们移除它,修改 app.vue 成这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<template>
  <div>
    <AppHeader />
    <main class="pt-[60px] min-h-[calc(100vh-60px)]">
      <div class="container mx-auto px-4 py-8">
        <h1 class="text-3xl font-bold">
          欢迎使用
        </h1>
        <p class="mt-4">
          这是一个支持暗黑模式的页面
        </p>
      </div>
    </main>
  </div>
</template>

就可以了。点击暗黑切换按钮试试吧。

代码还是在这里:https://github.com/wkii/nuxt-primevue-tailwindcss-starter