基于css变量轻松实现网站的主题切换功能

我们经常看到一些网站都有主题切换,例如vue官方文档。那他是怎么实现的呢?

检查元素,发现点击切换时,html元素会动态的添加和移除一个class:dark,然后页面主题色就变了。仔细想想,这要是放在以前,可能要写两套样式,就像这样:

css 复制代码
body {
  background-color: '#fff';
}
body.dark {
  background-color: '#000';
}

这写起来得多麻烦啊,而且难以维护。

好在我们有CSS变量 ,早在2017年,微软宣布Edge浏览器将支持CSS变量,现在几乎所有的浏览器都已经支持了这个功能。(IE:啊这?)

css变量也是变量,就像js一样,先声明,再读取。

css 复制代码
body {
  --text-color: red;
}
.box {
  color: var(--text-color);
}

已经出来很多年了,今天就不详细介绍了,有兴趣的推荐阅读 阮一峰老师的《CSS 变量教程》

今天就用vue3项目来写一个基于css变量实现的主题切换demo。

创建一个vue3项目:

sql 复制代码
npm create vue@latest

创建一个theme.css文件。

css 复制代码
/**
  *默认主题
  */
:root {
  --bg: #fff;
  --text-color: #000;
}
/**
  *添加属性,用来控制暗黑模式时的样式
  */
html[data-theme="dark"] {
  --bg: #000;
  --text-color: #fff;
}

或者像vue文档中一样使用class,如下所示:

css 复制代码
:root {
  --bg: #fff;
  --text-color: #000;
}
html.dark {
  --bg: #000;
  --text-color: #fff;
}

但是如果某个页面内无意中野使用到同名dark这个class,可能会造成影响,我这里还是用属性。

main.js中引入一下theme.css

js 复制代码
import './assets/theme.css'

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

App.vue style中调用一下变量,并动态改变data-theme的值:

html 复制代码
<template>
  <main>
    <p>主题切换demo</p>
    <button @click="change">切换</button>
  </main>
</template>
<script>
  let theme = 'light'
  const change = () => {
    theme = theme === 'light' ? 'dark' : 'light'
    document.documentElement.setAttribute('data-theme', theme)
  }
</script>

<style scoped>
main {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  background-color: var(--bg);
  color: var(--text-color);
}
p {
  margin: 20px 0;
}
</style>

看看效果:

功能基本上已经实现了,再来把这个切换操作封装成一个组件,并全局实时共享主题数据。

创建一个useTheme.js,用来执行设置属性的操作:

js 复制代码
import { ref, watchEffect } from 'vue'

// 默认用亮色
const theme = ref('light')

// 每次改变都设置一下
watchEffect(() => {
  document.documentElement.setAttribute('data-theme', theme.value)
})

export default function useTheme() {
  return {
    theme
  }
}

创建一个switch-theme.vue组件,仅仅用来改变theme的值:

html 复制代码
<template>
  <el-switch 
    v-model="theme"
    :active-action-icon="Moon"
    :inactive-action-icon="Sunny"
    active-color="#2f2f2f"
    active-value="dark"
    inactive-value="light"
    @change="changeDark"
  ></el-switch>
</template>

<script setup>
  import { Sunny, Moon } from '@element-plus/icons-vue'
  import useTheme from '../hooks/useTheme'
  const { theme } = useTheme()
  const changeDark = (data) => {
    theme.value === data
  }
</script>

改一下App.vue文件,引入并使用ThemeSwitch组件和useTheme Hook:

html 复制代码
<template>
  <main>
    <p>主题切换demo</p>
    <ThemeSwitch></ThemeSwitch>
    <p>当前主题:{{theme}}</p>
  </main>
</template>

<script setup>
  import ThemeSwitch from './components/theme-switch.vue'
  import useTheme from './hooks/useTheme'

  const { theme } = useTheme()
</script>

<style scoped>
main {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  background-color: var(--bg);
  color: var(--text-color);
}
p {
  margin: 20px 0;
}
</style>

再看看效果:

现在由一个专门的组件用来控制切换主题,并且不同组件内也都能共享theme变量了。

最后再优化一下,目前默认是亮色,切换到暗色以后再刷新页面,又会回到亮色,可以把theme变量存到localstorage

修改一下useTheme.js:

js 复制代码
import { ref, watchEffect } from 'vue'

// 从取缓存中取值,给个默认值。
const theme = ref(localStorage.getItem('theme') || 'light')

// 每次改变都设置一下属性,并存到缓存中。
watchEffect(() => {
  document.documentElement.setAttribute('data-theme', theme.value)
  localStorage.setItem('theme', theme.value)
})

export default function useTheme() {
  return {
    theme
  }
}

全部代码见Github

相关推荐
小李子呢02118 小时前
前端八股Vue---Vue2和Vue3的区别,set up的用法
前端·javascript·vue.js
邂逅星河浪漫9 小时前
【银行内网开发-管理端】Vue管理端+Auth后台开发+Nginx配置+Linux部署(详细解析)
linux·javascript·css·vue.js·nginx·html·前后端联调
一 乐9 小时前
电影院|基于springboot + vue电影院购票管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·电影院购票管理管理系统
奔跑的呱呱牛9 小时前
@giszhc/vue-page-motion:Vue3 路由动画怎么做才“丝滑”?(附在线示例)
前端·javascript·vue.js
一 乐11 小时前
旅游|基于springboot + vue旅游信息推荐系统(源码+数据库+文档)
java·vue.js·spring boot·论文·旅游·毕设·旅游信息推荐系统
最逗前端小白鼠12 小时前
vue3 数据响应式遇到的问题
前端·vue.js
卤蛋fg613 小时前
vxe-table 自定义数字行主键,解决默认字符串主键与后端类型不匹配问题
vue.js
岁月宁静13 小时前
都知道AI大模型能生成文本内容,那你知道大模型是怎样生成文本的吗?
前端·vue.js·人工智能
|晴 天|13 小时前
我如何用Vue 3打造一个现代化个人博客系统(性能提升52%)
前端·javascript·vue.js
yuqifang14 小时前
vue3+typescript+vite封装自己的UI组件库并上传至npm
vue.js·arkui