React 主题切换(方案分享🤩)

前段时间做了个小的个人起始页,一直在想加什么功能进去,也有添加主题切换的想法,但是由于该页面有点单一加上我并没有去调研过主题切换的方案,因此一直搁置。

这个项目的初衷是除了满足我个人使用浏览器的需求外,也想多学习一些前端知识,故而最近抽出时间调研了一下现有方案并在这个项目中实现一下。

在调研的方案中,我选取了如下四个方案:

  1. React Context
  2. CSS变量 + className切换
  3. 使用CSS变量 + CSSStyleDeclaration.setProperty()
  4. 特殊的canvas方案(展示)

其中方案3和方案4将主要应用在-->个人起始页实现,方案1和方案2作为小Demo在这里-->Demo

React Context

使用React Context可以有两种方式来实现主题切换(前提是要先会使用 Context 深层传递参数):

  1. 仅指定主题(即当前使用主题的名称),主题的具体样式在对应需要切换主题的组件中进行编写。
  2. 将主题的主要CSS,以CSSProperties(样式对象)的形式存入Context,在特定组件中使用Context变量。

实际上以上两种方式异曲同工,在此以 ① 举例。 首先在一个新的文件中创建Context并导出:

js 复制代码
import { createContext } from 'react';

//指定默认值
const defaultTheme = 'light';
//创建context
const ThemeContext = createContext(defaultTheme);
//导出
export default ThemeContext;

其次就是使用了,在项目的某个根组件处使用我们创建的ThemeContext.Provider,并设置状态:

js 复制代码
function App() {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={theme}>
      <Demo />
      <Button
        onClick={() => {
          setTheme(theme === 'light' ? 'dark' : 'light');
        }}
      >
        主题切换
      </Button>
    </ThemeContext.Provider>
  );
}

接下来我们只需要在需要切换样式的组件上获取Context中的值,并绑定相应的样式即可:

css 复制代码
.box{
  width: 200px;
  height: 200px;
}

.light{
  background-color: #fff;
  color: #333;
}

.dark{
  background-color: #333;
  color: #fff;
}
js 复制代码
import ThemeContext from './ThemeContext';
import './style.css';

export default function Demo() {
  const theme = useContext(ThemeContext);
  return <div className={`box ${theme}`}>Context Demo</div>;
}

如果你想像②中所述的以CSSProperties(样式对象)的形式存入Context,为了简化Context的使用,可以像这样封装:

js 复制代码
import { createContext, useContext, useMemo, useState } from "react";

const THEMES = {
  light: {
    text: '#145f32',
    cardBGColor: "#ffb002",
  },
  dark: {
    text: '#ffffff',
    cardBGColor: "#145f32",
  }
}

const themeValue = {
  theme: THEMES.light,
  toggleTheme: () => null,
  mode: "light",
}

const ThemeContext = createContext(themeValue)

export const useThemeContext = () => useContext(ThemeContext)

export const ThemeProvider = (props) => {
  const [mode, setMode] = useState("light")

  const theme = useMemo(() => {
    return mode === "light" ? THEMES.light : THEMES.dark
  }, [mode])

  const toggleTheme = () => {
    setMode(mode === "light" ? "dark" : "light")
  }

  return <ThemeContext.Provider {...props} value={{theme, toggleTheme, mode}}/>
}

CSS变量 + className

首先我们需要在根样式文件中定义好CSS变量以及对应主题类的变量取值:

css 复制代码
:root {
  /*默认白色主题取值*/
  --theme-color: #333;
  --theme-backgroundColor: #fff;
}

/*黑色主题对应的变量取值*/
.dark{
  --theme-color: #fff;
  --theme-backgroundColor: #333;
}

接下来在我们在需要切换主题的组件中使用CSS变量来指定样式:

css 复制代码
.box{
  width: 200px;
  height: 200px;
  background-color: var(--theme-backgroundColor);
  color: var(--theme-color);
}

最后我们只需要应用即可,这里的方案是.dark类名在<html>标签中添加/删除

js 复制代码
  <button
    onClick={() => {
      const classList = document.documentElement.classList;
      if (classList.value) {
        document.documentElement.classList.remove('dark');
        return;
      }
      document.documentElement.classList.add('dark');
    }}
  >
    主题切换
  </button>;

使用CSS变量 + CSSStyleDeclaration.setProperty()

首先我们需要学习一下 CSSStyleDeclaration.setProperty()

看一下MDN的描述,结合我们本节标题,可能你已经知道该怎么做了🤩。

此时我们需要在组件样式上使用变量来指定样式,之后只需要调用如下函数即可:

js 复制代码
export const setCssVar = (cssVar, value) => {
  document.documentElement.style.setProperty(cssVar, value);
};

此处将对应的CSS变量和值就会直接挂载到<html>标签上。

看到这里,你会发现,其实还有些方案是可以根据上述3点部分结合的,大家选择合适的就行!

特殊的canvas方案(展示)

由于内容已经很多了,为此我这里先给大家看看效果,下一篇会描述如何实现👻。

相关推荐
_殊途30 分钟前
HTML-CSS项目练习
前端·css·html
@AfeiyuO35 分钟前
el-table 表格嵌套表格
前端·elementui·vue
我是华为OD~HR~栗栗呀2 小时前
华为OD-23届-测试面经
java·前端·c++·python·华为od·华为·面试
β添砖java2 小时前
vivo响应式官网
前端·css·html·1024程序员节
web打印社区8 小时前
使用React如何静默打印页面:完整的前端打印解决方案
前端·javascript·vue.js·react.js·pdf·1024程序员节
喜欢踢足球的老罗8 小时前
[特殊字符] PM2 入门实战:从 0 到线上托管 React SPA
前端·react.js·前端框架
小光学长8 小时前
基于Vue的课程达成度分析系统t84pzgwk(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
Baklib梅梅9 小时前
探码科技再获“专精特新”认定:Baklib引领AI内容管理新方向
前端·ruby on rails·前端框架·ruby
南方以南_9 小时前
Chrome开发者工具
前端·chrome
YiHanXii9 小时前
this 输出题
前端·javascript·1024程序员节