Skip to content

lvqingxiang/multi-theme

Repository files navigation

MultiTheme

前提条件

  • Node: v16.13.1
  • Npm:v8.1.2
  • [sudo] npm install -g create-react-app
  • create-react-app [project-name] --typescript
    • cd [project-name]
    • npm install
    • npm run start
    • npm install antd --save

方案一

借助多个theme-class实现主题切换
  1. 配置less

    新创建的CRA项目不支持less,需要配置less-loader。此处选择[project-name]/config-overrides.js根目录创建config-overrides.js文件覆盖内部webpack配置,而不是run eject暴露webpack配置文件

    npm install less less-loader --save-dev

    npm install react-app-rewired customize-cra --save-dev

    [project-name]/package.json根目录package.json替换react-scripts => react-app-rewired

    less-loader版本>=8时,有几率报错"TypeError: this.getOptions is not a function",暂时选择7版本

    // package.json
    {
      "name": "mydemo",
      "version": "0.1.0",
      "private": true,
      "dependencies": {
        "@testing-library/jest-dom": "^5.16.0",
        "@testing-library/react": "^11.2.7",
        "@testing-library/user-event": "^12.8.3",
        "antd": "^4.17.2",
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "react-scripts": "4.0.3",
        "typescript": "^4.5.2",
        "web-vitals": "^1.1.2"
      },
      "scripts": {
        "start": "react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test",
        "eject": "react-scripts eject"
      },
      "eslintConfig": {
        "extends": [
          "react-app",
          "react-app/jest"
        ]
      },
      "browserslist": {
        "production": [
          ">0.2%",
          "not dead",
          "not op_mini all"
        ],
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ]
      },
      "devDependencies": {
        "@types/react": "^17.0.37",
        "@types/react-dom": "^17.0.11",
        "less": "^4.1.2",
        "less-loader": "^7.3.0",
        "react-app-rewired": "^2.1.8",
        "customize-cra": "^1.0.0"
      }
    }
    // config-overrides.js
    const { override, addLessLoader } = require("customize-cra");
    
    module.exports = override(addLessLoader({ lessOptions: {} }));
  2. 编写测试代码

    src/index.tsx作为入口文件

    import React from "react";
    import ReactDOM from "react-dom";
    
    import App from "./class-demo";
    
    ReactDOM.render(
      <React.StrictMode>
        <>
          <App />
        </>
      </React.StrictMode>,
      document.getElementById("root")
    );

    src/App.tsx作为展示文件,ThemeChange切换时,切换class

    import React from "react";
    import { Radio } from "antd";
    
    import "./law.css";
    import "./theme/index.css";
    import "./App.less";
    
    import ThemeChange from "./ThemeChange";
    
    const AppClass = () => {
      return (
        <>
          <div>文本</div>
          <button>按钮</button>
        </>
      );
    };
    
    const ThemeChange: React.FC<{
      value: string;
      onChange: (value: string) => void;
    }> = ({ value, onChange }) => {
      return (
        <Radio.Group
          optionType="button"
          buttonStyle="solid"
          value={value}
          options={[
            { label: "default", value: "default" },
            { label: "light", value: "light" },
            { label: "dark", value: "dark" },
          ]}
          onChange={(e) => onChange(e.target.value)}
        />
      );
    };
    
    const App = () => {
      const [theme, setTheme] = React.useState("default");
      return (
        <div className={theme}>
          <ThemeChange value={theme} onChange={setTheme} />
          <AppClass />
        </div>
      );
    };
    
    export default App;

    src/App.less作为样式文件

    button {
      background-color: var(--background-color);
      color: var(--font-color);
    }

    src/law.css定义基准颜色变量

    :root {
      /** default */
      --default-background-color: #4062bb;
      --default-font-color: #5d737e;
    
      /** light */
      --light-background-color: #7e7f9a;
      --light-font-color: #f6f2ff;
    
      /** dark */
      --dark-background-color: #52489c;
      --dark-font-size: #4b5358;
    }

    src/theme文件夹内分别定义lignt.css/dark.css/default.css,以index.css作为统一出口

    // index.css
    @import url("./dark.css");
    @import url("./default.css");
    @import url("./light.css");
    
    // dark.css
    .dark {
      --background-color: var(--dark-background-color);
      --font-color: var(--dark-font-color);
    }
    
    // light.css
    .light {
      --background-color: var(--light-background-color);
      --font-color: var(--light-font-color);
    }
    
    // default.css
    .default {
      --background-color: var(--default-background-color);
      --font-color: var(--default-font-color);
    }

    切换radio按钮样式将会造成按照主题色进行改变

  3. 引入antd组件改造

    npm install babel-plugin-import

    更新config-overrides.js文件

    const { override, addLessLoader, fixBabelImports } = require("customize-cra");
    
    module.exports = override(
      addLessLoader({ lessOptions: { javascriptEnabled: true } }),
      fixBabelImports("import", {
        libraryName: "antd",
        libraryDirectory: "es",
        style: true,
      })
    );

    src/App.less增加语句@import "~antd/dist/antd.less";和样式

    @import "~antd/dist/antd.less";
    
    .app {
      color: var(--font-color);
      button {
        background-color: var(--background-color);
      }
    }
    
    .antd-app {
      button {
        color: var(--font-color);
      }
      .ant-picker:hover,
      .ant-picker-focused {
        border-color: var(--background-color);
      }
    }

    src/App.tsx引入Antd依赖import { Button, DatePicker } from "antd";,修改AppClass

    import { Button, DatePicker } from "antd";
    
    const AppClass = () => {
      return (
        <>
          <div className="app">
            <p>文本</p>
            <button>按钮</button>
          </div>
          <br />
          <div className="antd-app">
            <Button type="link">组件Button</Button>
            <DatePicker />
          </div>
        </>
      );
    };

    方案二

    单个主题ModifyVars覆盖

    如果只是希望定制某一种特定主题,可以参考Antd定制主题方案。例如通过less提供的ModifyVars的方式进行Antd组件的样式覆盖。

    config-overrides.js增加配置

    const { override, addLessLoader, fixBabelImports } = require("customize-cra");
    
    module.exports = override(
      fixBabelImports("import", {
        libraryName: "antd",
        libraryDirectory: "es",
        style: true,
      }),
      addLessLoader({
        lessOptions: {
          javascriptEnabled: true,
          modifyVars: { "primary-color": "#52489c" }, // 示例
        },
      })
    );

    App.less中去掉对于Antd组件的样式设置

    @import "~antd/dist/antd.less";
    
    .app {
      color: var(--font-color);
      button {
        background-color: var(--background-color);
      }
    }

​ 重新启动项目,因为config-overrides.js是预编译,不会引发热更新,重新启动项目,即可看到效果。

About

探索React 多主题 颜色变换的方案

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published