前端暗亮主题切换🚀

1,390 阅读5分钟

前言

我们在看一些官网的时候总能看到一些关于暗亮主题切换的功能,比如vue3的官方文档。 image.png

Link标签动态引入

Link标签动态引入的优点是- 实现了按需加载,提高了首屏加载时的[性能] 缺点是: 最开始我们可以准备好几套css主题文件。

  • 动态加载样式文件,如果文件过大网络情况不佳的情况下可能会有加载延迟,导致样式切换不流畅
  • 如果主题样式表内定义不当,会有优先级问题
  • 各个主题样式是写死的,后续针对某一主题样式表修改或者新增主题也很麻烦

image.png

我想通过点击灯泡来切换背景颜色

image.png

代码实现

<i ref="themeIcon" class="iconfont icon-dengpao1" 
style="color: red; font-size: 20px">
</i>
import { ref, onMounted } from 'vue';  
  
// 定义一个 ref 来引用主题图标  
const themeIcon = ref(null);  
  
// 定义当前主题  
let currentTheme = ref('blue');  
  
// 定义切换主题的函数  
function switchTheme() {  
let themeLink = document.getElementById('theme-link');  
  
if (!themeLink) {  
// 创建新的 <link> 元素  
themeLink = document.createElement('link');  
themeLink.id = 'theme-link';  
themeLink.rel = 'stylesheet';  
themeLink.type = 'text/css';  
document.head.appendChild(themeLink);  
}  
// 切换主题样式  
if (currentTheme.value === 'blue') {  
themeLink.href = new URL('@/css/green-theme.css', import.meta.url).href;  
currentTheme.value = 'green';  
} else {  
themeLink.href = new URL('@/css/blue-theme.css', import.meta.url).href;  
currentTheme.value = 'blue';  
}  
}  
  
// 在组件挂载后添加点击事件监听器  
onMounted(() => {  
themeIcon.value.addEventListener('click', switchTheme);  
});

image.png

image.png //首先通过const themeIcon=ref('null')这样我们就引入了一个主题图标。

  • themeIcon:这里我们使用 ref 创建了一个名为 themeIcon 的响应式引用,初始值为 null。这个引用将指向我们在模板中通过 ref="themeIcon" 标记的 DOM 元素(即图标元素)。通过这种方式,我们可以在 JavaScript 代码中直接访问这个图标元素。

let currentTheme=ref('blue')

currentTheme:我们再次使用 ref 创建了一个名为 currentTheme 的响应式引用,初始值为 'blue'。这个变量用于跟踪当前的主题(蓝色或绿色),并在切换主题时更新其值。 好啦这是基本变量的定义。

接下来就是定义一个 switchTheme 函数

function switchTheme() {
let themeLink = document.getElementById('theme-link'); 
if (!themeLink) { 
// 创建新的 <link> 元素
themeLink = document.createElement('link');
themeLink.id = 'theme-link'; 
themeLink.rel = 'stylesheet';
themeLink.type = 'text/css';
document.head.appendChild(themeLink);
} 
// 切换主题样式
  if (currentTheme.value === 'blue') { 
   themeLink.href = new URL('@/css/green-theme.css', import.meta.url).href;   
   currentTheme.value = 'green';
} else {
  themeLink.href = new URL('@/css/blue-theme.css', import.meta.url).href;    currentTheme.value = 'blue'; 
 }
}

let themeLink = document.getElementById('theme-link');这段代码是用来获取具有id为theme-link的元素。如果该元素不存在的话,那么就创建该元素。并且给它的属性加上id,rel,type。document.head.appendChild(themeLink);用于将新创建的 DOM 元素(如 <link> 标签)添加到文档的 <head> 部分的方法。

if (currentTheme.value === 'blue'):这行代码检查当前的主题是否为蓝色。如果 currentTheme.value 的值是 'blue',则执行 if 块中的代码,切换到绿色主题;否则,执行 else 块中的代码,切换回蓝色主题。 new URL('@/css/green-theme.css', import.meta.url).href

new URL():这是一个构造函数,用于创建一个新的 URL 对象。它接受两个参数:

第一个参数是你要解析的路径(在这个例子中是 @/css/green-theme.css)。

第二个参数是基础 URL(在这个例子中是 import.meta.url),它提供了当前模块的 URL,确保路径解析的正确性。

import.meta.url:这是 Vite 和 Webpack 等现代构建工具提供的一个属性,表示当前模块的 URL。它确保了即使在不同的开发环境中,路径解析也能正确进行。

.href:这是 URL 对象的一个属性,返回完整的 URL 字符串。我们将这个字符串赋值给 themeLink.href,以便浏览器加载相应的 CSS 文件。

  • '@/css/green-theme.css' :这是一个相对路径或别名,指向项目中的某个 CSS 文件。
  • import.meta.url:这是当前模块的绝对 URL。假设当前模块的路径是 http://localhost:3000/src/components/ThemeSwitcher.vue,那么 import.meta.url 的值就是这个 URL。
  • new URL() :它会根据 import.meta.url 将相对路径 @/css/green-theme.css 转换为绝对路径,例如 http://localhost:3000/src/css/green-theme.css

最后执行 onMounted(() => {
themeIcon.value.addEventListener('click', switchTheme);
}); 由于组件挂载之前,Vue 可能还没有将模板中的元素渲染到页面中。如果你直接在组件初始化时尝试操作 DOM 元素(例如通过 ref 获取元素),可能会导致 themeIcon.valuenullundefined,因为此时元素还没有被渲染到页面上。这可以避免这个问题。

提前引入所有主题样式,做类名切换

先提一嘴,<style> 标签中没有使用 scoped 属性的样式会被视为全局样式,这意味着这些样式会应用于整个项目中的所有组件和页面,而不仅仅局限于当前组件。

<style lang="scss">  
/* 全局样式 */  
body {  
transition: background-color 0.3s, color 0.3s; /* 添加过渡效果 */  
}  
  
/* 亮色主题 */  
body.light {  
background-color: #fff !important; /* 白色背景 */  
color: #333 !important; /* 深灰色文本 */  
}  
  
/* 暗色主题 */  
body.dark {  
background-color: #333 !important; /* 深灰色背景 */  
color: #eee !important; /* 浅灰色文本 */  
}  
  
/* 特定元素在亮色主题下的样式 */  
body.light .box {  
color: #f90 !important; /* 橙色文本 */  
background: #fff !important; /* 白色背景 */  
}  
</style>

为了方便使用我将它封装到了

image.png 并且:在main.js里边

image.png

好啦话不多说我们来看看下边的代码是如何实现的吧

<template>  
<div class="parent">  
<div class="son1">  
<div>  
<h1 style="display: inline-block; font-size: 20px">Introducing Claude, AI Assistant</h1>  
<i class="iconfont icon-xiajiantou" style="font-size: 18px"></i>  
</div>  
</div>  
<div class="son2">  
<i class="iconfont icon-star" style="font-size: 25px"></i>  
<i class="iconfont icon-luntaituoyin" style="font-size: 25px"></i>  
<i class="iconfont icon-xiaoxi" style="font-size: 25px"></i>  
<i class="iconfont icon-dengpao1" @click="switchTheme" style="color: red; font-size: 20px"></i>  
</div>  
</div>  
</template>  
<script setup>  
import { ref, onMounted } from 'vue';  
// 定义当前主题  
const currentTheme = ref('light'); // 默认为亮色主题  
// 定义切换主题的函数  
function switchTheme() {  
// 切换主题  
const newTheme = currentTheme.value === 'light' ? 'dark' : 'light';  
  
// 使用 classList API 安全地切换类名  
document.body.classList.toggle('light', newTheme === 'light');  
document.body.classList.toggle('dark', newTheme === 'dark');  
  
// 更新当前主题状态  
currentTheme.value = newTheme;  
  
// 打印日志以确认切换  
console.log(`Switched to ${newTheme} theme`);  
}  
  
// 在组件挂载时设置初始主题  
onMounted(() => {  
// 确保初始主题正确应用  
document.body.classList.add(currentTheme.value);  
});  
</script>  
  
<style lang="scss">  
/* 全局样式 */  
body {  
transition: background-color 0.3s, color 0.3s; /* 添加过渡效果 */  
}  
  
/* 亮色主题 */  
body.light {  
background-color: #fff !important; /* 白色背景 */  
color: #333 !important; /* 深灰色文本 */  
}  
  
/* 暗色主题 */  
body.dark {  
background-color: #333 !important; /* 深灰色背景 */  
color: #eee !important; /* 浅灰色文本 */  
}  
  
/* 特定元素在亮色主题下的样式 */  
body.light .box {  
color: #f90 !important; /* 橙色文本 */  
background: #fff !important; /* 白色背景 */  
}  
</style>  
  
<style lang="scss" scoped>  
.parent {  
display: flex;  
justify-content: flex-end;  
align-items: center; /* 垂直居中 */  
}  
  
.son1 {  
width: 90%;  
display: flex;  
justify-content: center;  
}  
  
.son2 {  
display: flex;  
width: 10%;  
justify-content: space-between;  
}  
  
.iconfont {  
font-size: 16px;  
font-style: normal;  
-webkit-font-smoothing: antialiased;  
-moz-osx-font-smoothing: grayscale;  
}  
</style>

image.png

image.png 点击后的颜色切换。 核心代码是如下:

function switchTheme() {  
// 切换主题  
const newTheme = currentTheme.value === 'light' ? 'dark' : 'light';  
  
// 使用 classList API 安全地切换类名  
document.body.classList.toggle('light', newTheme === 'light');  
document.body.classList.toggle('dark', newTheme === 'dark');  
  
// 更新当前主题状态  
currentTheme.value = newTheme;  
  
// 打印日志以确认切换  
console.log(`Switched to ${newTheme} theme`);  
}//递归思想

由于先是const currentTheme = ref('light'); // 默认为亮色主题 设置为了亮色主题。 当点击后const newTheme = currentTheme.value === 'light' ? 'dark' : 'light';就会设置一个newTheme。 document.body.classList.toggle('light', newTheme === 'light');
document.body.classList.toggle('dark', newTheme === 'dark'); 通过这两段代码来进行类名切换。

currentTheme.value = newTheme;又进行一次重置。