乐趣区

关于react.js:前端实现切换主题方案

前端开发人员面临着制作加强用户体验和适应用户偏好的用户界面的工作。带有 cssreact 次要可用于创立多色可切换主题。为用户提供了在给定工夫点在主题色彩之间切换以适宜他们的偏好的特权。

介绍

在本文中,咱们将探讨应用 reactless 和其余依赖项构建多色主题的各种步骤和要求。目前市面上用的多的就两种计划:

  1. React-styled-components
  2. 全局保护两套主题色(如果只有 lightdark 两种主题)以及全局的 root 变量以供组件援用,配合自定义 context 实现不同主题切换性能

集体不喜爱 styled-components 这种办法,款式、构造、脚本混在一起感觉有点顺当,感兴趣的能够本人去尝试,本文次要介绍第二种办法,并且配合 react 比拟风行的组件库antd 进行开发。

前置常识

读者应该有 reactless 和他们抉择的任何设计库的教训。如果您对上述语言或框架不称心,请在持续之前花点工夫理解基础知识。

React 中主题处理程序的组件

要实现构建可切换多色主题的指标,您的 Web 应用程序必须具备以下组件:

  • 用于色彩状态治理的 Context Api
  • 导航栏 (主题切换按钮)
  • 页面元素的设计依赖项
  • 不同主题的全局 less 文件

架构图

开始应用 React 进行主题开发

第 1 步:创立 React 应用程序

每次开发的第一步 react 都是创立 react 应用程序。这一步很容易实现。要创立您的 react 应用程序,请在命令终端中运行如下所示的命令:

npx create-react-app demo-app
# or
yarn create-react-app demo-app

下面的 command 代码片段将创立您的 react 应用程序,装置默认的开发依赖项,并最终提供一个样板来开始您的利用程序开发。

第 2 步:编译 Antd 两种主题下的 less 文件

antd 主题配置大多数状况下咱们会在 webpack 配置文件中配置对应 less loader 批改其中的 less 变量来实现根底的主题色等的自定义需要,其中 antd 还给咱们提供了一种配置的 context api,能够对组件配置国际化,自定义主题等性能。其中自定义主题(款式)能够通过批改内置的款式来实现,默认状况下,antd 组件内的款式名都是 ant 结尾,能够通过官网提供的 ConfigProvider 来批改这一配置,即批改 prefixCls 属性。这样咱们能够通过运行时动静批改款式名前缀(白天模式跟黑夜模式各设置一个前缀)来实现白天跟黑夜模式的切换。antd 曾经把 dark 模式的款式给咱们了,当初,咱们先来把两套款式文件提取进去放在咱们我的项目中本人保护,这也是为了咱们更加不便地去批改咱们自定义所须要的色彩而不必通过批改全局款式去笼罩原有款式,这样即不必放心款式层级问题,也升高了保护老本。

留神:如果未装置 less,则须要装置 less:npm install -g less

进入我的项目根目录,执行以下命令:

# 白天模式
lessc --js --modify-var="ant-prefix=custom-light" node_modules/antd/dist/antd.variable.less custom-light.css

# 黑夜模式
lessc --js --modify-var="ant-prefix=custom-dark" node_modules/antd/dist/antd.variable.less custom-dark.css

执行完会在我的项目根目录(脚本执行时的目录)生成两份 css 文件,为了后续开发不便,能够把这两份 css 后缀改为 less。如图:

其中,global.less 用于保留全局款式变量

第 3 步:创立主题上下文

theme/context 目录下新建一个用于解决全局 theme mode 变量、模式初始化、以及模式切换逻辑的 Provider,其中重要的逻辑有以下三点:

  1. 初始化以后用户保留的模式,从本地缓存中查找,没有的话走最初始用户没设置过的逻辑——追随以后用户零碎的模式,重要知识点:

    // 匹配浏览器的主题
    window.matchMedia('(prefers-color-scheme: light)')
    
    // 获取初始主题模式
    const getThemeMode = () => {return window.matchMedia('(prefers-color-scheme: light)').matches
        ? 'light'
        : 'dark'
    }
    
    // 监听浏览器(零碎)主题变动
    const matchMediaHandler = window.matchMedia('(prefers-color-scheme: light)')
    matchMediaHandler.addEventListener('change', changeColorMode)
  2. 每次用户手动切换主题后就追随用户习惯走

    const localKey = 'theme-mode'
    
    const getStorage = () => {const value = localStorage.getItem(localKey)
      return value ? value : getThemeMode()}
  1. 裸露给开发人员以后的 mode 以及切换 modeswitch办法

    const switchModeHandler = (value: string) => {const doc = document.querySelector('body')
      if (doc) {doc.removeAttribute('class')
        doc.classList.add(value)
        setStorage(value)
        setMode(value)
      }
    }
    
    const contextValue: ThemeContextType = {
      mode: mode,
      switchMode: switchModeHandler
    }
  1. 包裹 antd 提供的 ConfigProvider,依据 mode 的变幻动静传值批改 prefixCls 的值以动静批改组件内的款式

    return (
      // 自定义全局保护的主题
      <ThemeContext.Provider value={contextValue}>
        {/*antd 的主题 */}
        <ConfigProvider prefixCls={`custom-${mode}`}>
          {props.children}
        </ConfigProvider>
      </ThemeContext.Provider>
    )

残缺代码如下:

import React, {createContext, useEffect, useState} from 'react'
import {ConfigProvider} from 'antd'

const localKey = 'theme-mode'

const getStorage = () => {const value = localStorage.getItem(localKey)
  return value ? value : getThemeMode()}

const setStorage = (value: string) => {localStorage.removeItem(localKey)
  localStorage.setItem(localKey, value)
}

const getThemeMode = () => {return window.matchMedia('(prefers-color-scheme: light)').matches
    ? 'light'
    : 'dark'
}

// CONTEXT
interface ThemeContextType {
  mode: string
  switchMode: (value: string) => void
}

export const ThemeContext = createContext<ThemeContextType>({
  mode: '',
  switchMode: () => {}
})

// PROVIDER
export const ThemeContextProvider: React.FC<{children: JSX.Element}> = props => {const [mode, setMode] = useState('')

  useEffect(() => {const changeColorMode = () => {const color = getThemeMode()
      switchModeHandler(color)
    }

    const matchMediaHandler = window.matchMedia('(prefers-color-scheme: light)')
    matchMediaHandler.addEventListener('change', changeColorMode)

    switchModeHandler(getStorage())

    return () =>
      matchMediaHandler.removeEventListener('change', changeColorMode)
  }, [])

  const switchModeHandler = (value: string) => {const doc = document.querySelector('body')
    if (doc) {doc.removeAttribute('class')
      doc.classList.add(value)
      setStorage(value)
      setMode(value)
    }
  }

  const contextValue: ThemeContextType = {
    mode: mode,
    switchMode: switchModeHandler
  }

  return (
    // 自定义全局保护的主题
    <ThemeContext.Provider value={contextValue}>
      {/*antd 的主题 */}
      <ConfigProvider prefixCls={`custom-${mode}`}>
        {props.children}
      </ConfigProvider>
    </ThemeContext.Provider>
  )
}

### 第 4 步:创立 UI 界面

第 5 步:提供界面组件须要的全局款式变量

theme/styles/global.less 新增如下变量:

:root {
  --light-color: #fff;
  --dark-color: #000;
}

body {
  &.light {--default-bg: var(--light-color);
    --default-text-color: var(--dark-color);
  }

  &.dark {--default-bg: var(--dark-color);
    --default-text-color: var(--light-color);
  }
}

利用全局款式变量,如 NavBar 组件的款式文件中(其余组件同理):

.container {background: var(--default-bg);
    color: var(--default-text-color);
}

第 6 步:切换主题

NavBar 组件中:

import React, {type FC, useContext} from 'react'
import cls from 'classnames'

import {SearchBar} from '@common/components'
import {ThemeContext} from '@src/theme/context'

import styles from './index.module.less'

const NavBar: FC = () => {const { mode, switchMode} = useContext(ThemeContext)

  return (
    <div
      className={cls(styles.container, styles[mode])}
    >
      <div className={styles.relativeWrap}>
        <div
          className={styles.themeSwitch}
          onClick={() => switchMode(mode === 'dark' ? 'light' : 'dark')}
        >
          <img
            src={
              mode === 'dark'
                ? 'https://assets.blocksec.com/image/1664446020484-3.svg'
                : 'https://assets.blocksec.com/image/1664446020484-2.svg'
            }
            alt=""
          />
        </div>
      </div>
    </div>
  )
}

export default NavBar

论断

在本文中,咱们利用了各种概念来应用 React 创立多主题。咱们还应用react-context-api.

本文涵盖的概念可用于将来波及色彩和主题治理的我的项目。

我心愿您发现这些内容对您的编程工作有帮忙。

退出移动版