使用 CSS 变量和 React Context 的主题

2025-06-04

使用 CSS 变量和 React Context 的主题

CSS 变量真的很棒。你可以用它们做很多事情,其中​​之一就是轻松地在应用程序中应用主题。在本教程中,我将向你展示如何将它们与 React 集成,以创建一个ThemeComponent(带有上下文的)主题。

Gist 中的 CSS 变量

因此首先,我想简单解释一下什么是 CSS 变量(或者正式名称 - CSS 自定义属性),以及如何使用它们。

CSS 变量是我们定义变量的一种方式,它将在整个应用程序中应用。语法如下:

CSS 变量

这里发生了什么?

使用--{varName}符号,我们可以告诉浏览器存储一个名为varName(或在上例中为primary)的唯一变量,然后我们可以在文件var(--{varName})中的任何位置使用该符号.css

看起来真的很简单?因为它确实如此。其实没什么特别的。根据caniuse.com的统计,全球超过 92% 的用户使用支持 CSS 变量的浏览器(除非你真的需要 IE 支持,那样你就没那么幸运了),所以在大多数情况下,CSS 变量的使用是完全安全的。

如果您想了解更多,您可以在MDN 页面中找到更多信息。

从 JavaScript 设置 CSS 变量

在 JavaScript 中设置和使用 CSS 变量与在 CSS 中设置和使用它们一样简单。要获取元素上定义的值:

const primary = getComputedStyle(element).getPropertyValue("--primary");
Enter fullscreen mode Exit fullscreen mode

primary将为我们提供为定义的自定义 css 属性的值element

设置自定义 CSS 属性的工作方式如下:

element.style.setProperty("--light", "#5cd2b6");
Enter fullscreen mode Exit fullscreen mode

或者,如果我们想为整个应用程序设置属性,我们可以这样做:

document.documentElement.style.setProperty("--light", "#5cd2b6");
Enter fullscreen mode Exit fullscreen mode

现在light我们所有的代码都可以访问该属性。

Gist 中的 React Context

React Context API是 React 提供的唯一将 props 从一个组件间接传递给后代组件的方法。在本指南中,我将使用useContexthook(您可以在此处阅读更多相关信息),但其原理与类组件相同。

首先,我们必须初始化一个上下文对象:

import React from "react";

export const ThemeSelectorContext = React.createContext({
  themeName: "dark"
});
Enter fullscreen mode Exit fullscreen mode

传递给函数的参数React.createContext是上下文的默认参数。现在我们有了一个上下文对象,我们可以使用它来将 props “注入” 到我们的间接后代中:

export default ({ children }) => (
  <ThemeSelectorContext.Provider value={{ themeName: "dark" }}>
    {children}
  </ThemeSelectorContext.Provider>
);
Enter fullscreen mode Exit fullscreen mode

现在,任何想要读取我们上下文中的值的人都可以这样做:

import React, { useContext } from "react";
import { ThemeSelectorContext } from "./themer";

export const () => {
  const { themeName } = useContext(ThemeSelectorContext);

  return <div>My theme is {themeName}</div>
};
Enter fullscreen mode Exit fullscreen mode

瞧!无论我们的组件位于组件层次结构的哪个位置,它都可以访问该themeName变量。如果我们想允许在上下文中编辑该值,可以传递如下函数:

export default ({ children }) => {
  const [themeName, setThemeName] = useState("dark");

  const toggleTheme = () => {
    themeName === "dark" ? setThemeName("light") : setThemeName("dark");
  };

  <ThemeSelectorContext.Provider value={{ themeName, toggleTheme }}>
    {children}
  </ThemeSelectorContext.Provider>;
};
Enter fullscreen mode Exit fullscreen mode

如何使用它:

import React, { useContext } from "react";
import { ThemeSelectorContext } from "./themer";

export const () => {
  const { themeName, toggleTheme } = useContext(ThemeSelectorContext);

  return <>
    <div>My theme is {themeName}</div>
    <button onClick={toggleTheme}>Change Theme!</button>
  </>
};
Enter fullscreen mode Exit fullscreen mode

这足以满足我们的需求,但如果您愿意,可以进一步阅读官方 React Context 文档

把所有东西放在一起

现在我们知道了如何在 JavaScript 中设置 CSS 自定义属性,并且可以将 props 传递到组件树中,我们可以为应用程序创建一个非常漂亮且简单的“主题引擎”。首先,我们来定义主题:

const themes = {
  dark: {
    primary: "#1ca086",
    separatorColor: "rgba(255,255,255,0.20)",
    textColor: "white",
    backgroundColor: "#121212",
    headerBackgroundColor: "rgba(255,255,255,0.05)",
    blockquoteColor: "rgba(255,255,255,0.20)",
    icon: "white"
  },
  light: {
    primary: "#1ca086",
    separatorColor: "rgba(0,0,0,0.08)",
    textColor: "black",
    backgroundColor: "white",
    headerBackgroundColor: "#f6f6f6",
    blockquoteColor: "rgba(0,0,0,0.80)",
    icon: "#121212"
  }
};
Enter fullscreen mode Exit fullscreen mode

这恰好是我在博客中使用的调色板,但说到主题,天空才是极限,所以请随意尝试。

现在我们创建我们的ThemeSelectorContext

export const ThemeSelectorContext = React.createContext({
  themeName: "dark",
  toggleTheme: () => {}
});
Enter fullscreen mode Exit fullscreen mode

我们的主题组件:

export default ({ children }) => {
  const [themeName, setThemeName] = useState("dark");
  const [theme, setTheme] = useState(themes[themeName]);

  const toggleTheme = () => {
    if (theme === themes.dark) {
      setTheme(themes.light);
      setThemeName("light");
    } else {
      setTheme(themes.dark);
      setThemeName("dark");
    }
  };

  return (
    <ThemeSelectorContext.Provider value={{ toggleTheme, themeName }}>
      {children}
    </ThemeSelectorContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

在这个组件中,我们存储了我们选择的主题对象和选择的主题名称,并且我们定义了一个函数来切换我们选择的主题。

剩下的最后一步实际上是从主题设置 CSS 自定义属性。我们可以使用.style.setPropertyAPI 轻松完成:

const setCSSVariables = theme => {
  for (const value in theme) {
    document.documentElement.style.setProperty(`--${value}`, theme[value]);
  }
};
Enter fullscreen mode Exit fullscreen mode

现在,对于对象中的每个值,我们都可以访问具有相同名称(当然theme带有前缀)的 css 属性。我们最不需要的就是每次切换主题时都运行该函数,因此在我们的组件中,我们可以像这样使用钩子:--setCSSVariablesThemeuseEffect

export default ({ children }) => {
  // code...

  useEffect(() => {
    setCSSVariables(theme);
  });

  // code...
};
Enter fullscreen mode Exit fullscreen mode

完整源代码可以在 github 上找到

使用我们的主题非常方便:

.title {
  color: var(--primary);
}
Enter fullscreen mode Exit fullscreen mode

更新我们的主题也同样简单:

import Toggle from "react-toggle";

export default () => {
  const { toggleTheme, themeName } = useContext(ThemeSelectorContext);

  <Toggle defaultChecked={themeName === "dark"} onClick={toggleTheme} />;
};
Enter fullscreen mode Exit fullscreen mode

在这个例子中,我使用了Toggle中的组件react-toggle,但任何切换/按钮组件都可以。点击Toggle将调用该toggleTheme函数,并更新整个应用的主题,无需其他配置。

就是这样!这就是你需要为你的应用创建一个超级简洁、超级干净的主题引擎的全部步骤。如果你想看一个实际的示例,可以查看我博客的源代码。

感谢您的阅读,希望您喜欢!

文章来源:https://dev.to/dorshinar/themes-using-css-variables-and-react-context-3e20
PREV
Next.js - React 的未来?为什么 React 并不完整?探索 Next.js 摘要
NEXT
2020 年如何发布 NPM 软件包 简介 逐步总结