如何使用TailwindCSS + Vue3 + Vite实现暗黑模式切换

3

我是一个Vite/Vue3的初学者,目前面临一个问题,需要社区的综合知识支持。

我创建了一个Vite/Vue3应用,并安装了TailwindCSS:

npm create vite@latest my-vite-vue-app -- --template vue
cd my-vite-vue-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

然后我按照Tailwind主页上的说明进行操作:

在tailwind.config.js文件中添加所有模板文件的路径。
在main.js文件中导入新创建的./src/index.css文件。
创建一个./src/index.css文件,并为Tailwind的每个层添加@tailwind指令。

现在我有一个可工作的Vite/Vue3/TailwindCSS应用程序,想要添加切换深色模式的功能。

Tailwind文档表示,可以通过将darkMode: 'class'添加到tailwind.config.js,然后为<html>标签切换dark类来实现此目的。

我使用以下代码使其工作:

  1. index.html内部
<html lang="en" id="html-root">
  (...)
  <body class="antialiased text-slate-500 dark:text-slate-400 bg-white dark:bg-slate-900">
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
  1. About.vue 内部
<template>
  <div>
    <h1>This is an about page</h1>
    <button @click="toggleDarkMode">Toggle</botton>
  </div>
</template>

<script>
  export default {
    methods: {
      toggleDarkMode() {
        const element = document.getElementById('html-root')
        if (element.classList.contains('dark')) {
          element.classList.remove('dark')
        } else {
          element.classList.add('dark')
        }
      },
    },
  };
</script>

是的,我知道这不是Vue3风格的代码。当然,我也知道可以使用element.classList.toggle()代替.remove().add()。但或许将来会有像我一样的初学者查看这个代码,并且感激起点简单易懂的代码。所以请宽容一点...

现在我最终要问社区的问题:

我知道像这样操作DOM并不是Vue的做法。当然,我想以正确的方式实现我的目标。但我该怎么做呢?

相信我,我谷歌了好几个小时,但没有找到一个解决方案,而不需要安装这个和那个和那个额外的npm模块。

但我想采取极简主义的方法。尽可能少的依赖项,以免使我和其他想开始学习的人感到不知所措。

在这样的背景下,你们有没有解决办法给我和其他新手呢? :-)

2个回答

2
你的事件目标元素在应用程序之外。这意味着除了通过DOM可用方法查询它以外,没有其他与之交互的方式。
换句话说, 你做得很对。如果元素在应用程序中,那么你只需将类链接到属性上,并让Vue处理DOM操作的具体细节:
 :class="{ dark: darkMode }"

但事实并非如此。


另外需要注意的是,你的切换方法不应该依赖于 <body> 元素是否具有该类来决定是否应该应用或删除它。你应该将值保存在应用程序的状态中,并且这应该是你唯一的真相来源。
这是 Vue 的原则,你不想打破它:让数据驱动 DOM 状态,而不是反过来。

可以从 <body> 当前状态(挂载时)中获取值,但从那时起,应用程序状态的更改将决定元素上是否存在该类。

Vue2 示例:

Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
  el: '#app',
  data: () => ({
    darkMode: document.body.classList.contains('dark')
  }),
  methods: {
    applyDarkMode() {
      document.body.classList[
        this.darkMode ? 'add' : 'remove'
      ]('dark')
    }
  },
  watch: {
    darkMode: 'applyDarkMode'
  }
})
body.dark {
  background-color: #191919;
  color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id="app">
  <label>
    <input type="checkbox" v-model="darkMode">
    dark mode
  </label>
</div>

Vue3 示例:

const {
  createApp,
  ref,
  watchEffect
} = Vue;

createApp({
  setup() {
    const darkMode = ref(document.body.classList.contains('dark'));
    const applyDarkMode = () => document.body.classList[
      darkMode.value ? 'add' : 'remove'
    ]('dark');
    watchEffect(applyDarkMode);
    return { darkMode };
  }
}).mount('#app')
body.dark {
  background-color: #191919;
  color: white;
}
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>

<div id="app">
  <label>
    <input type="checkbox" v-model="darkMode">
    dark mode
  </label>
</div>

显然,如果您在多个组件中使用darkMode,您可能希望将其状态保存在某个外部存储中,而不是本地的data中(并通过computed在组件中提供它)。

1
您要查找的是绑定类,但您卡住的地方是尝试操作在主Vue实例中挂载的<div>之外的<body>
现在您的问题可能是您的按钮位于与根<div id="app">不同的文件中,该文件从样板代码的App.vue开始。您有两个解决方案:研究状态管理(更适合可扩展性),或在父组件和子组件之间进行一些简单的变量传递。我将展示后者:
从你的切换组件开始:
// DarkButton.vue
<template>
  <div>
    <h1>This is an about page</h1>
    <button @click="toggleDarkMode">Toggle</button>
  </div>
</template>

<script>
export default {
  methods: {
    toggleDarkMode() {
      this.$emit('dark-switch');
    },
  },
};
</script>

这里使用了组件事件($emit)Vue.js官方文档

接着,你的父级/根组件App.vue会监听该toggle事件并以Vue方式更新其class:

<template>
  <div id="app" :class="{ dark: darkmode }">
    <p>Darkmode: {{ darkmode }}</p>
    <DarkButton @dark-switch="onDarkSwitch" />
  </div>
</template>

<script>
import DarkButton from './components/DarkButton.vue';

export default {
  name: 'App',
  components: {
    DarkButton,
  },
  data: () => ({
    darkmode: false,
  }),
  methods: {
    onDarkSwitch() {
      this.darkmode = !this.darkmode;
    },
  },
};
</script>

虽然Tailwind建议将Vanilla JS添加到您的<body>中,但通常不应从该点开始进行操作。相反,不要操纵您的<body>,只需在您想让Vue控制的范围内使用<div id="app">即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接