美文网首页
react + antd 动态切换主题 + 局部样式

react + antd 动态切换主题 + 局部样式

作者: HopeLii | 来源:发表于2020-03-03 01:48 被阅读0次

    看了antd官网的支持自定义主题,但是不支持动态切换。然后看到antd-pro支持动态切换主题,但要配合UmiJS。嗯......

    后面又搜到了两个插件(其实就是同一个 -_- )
    demo

    1. antd-theme-generator
    2. antd-theme-webpack-plugin

    本文的动态主题实现,就是按照第一个插件来做的,配置过程中,也有坑,因此整理了笔记。

    1. 原理:通过less.js来进行modifyVars操作
    2. 插件中提供的案例通过config-overrify.js来配置,但是无法使用局部样式,因为create-react-app脚手架虽然支持Css Module,但是webpack并没有配置less-loader, 所以需要手动配置,既然暴露了webpack.config.js,那么索性就全在里面配置了,不在config-overrify.js中配置了。

    接下来我们开始配置动态主题

    版本号

    "antd": "^4.0.0",
    "react": "^16.13.0",
    "webpack": "4.41.5",
    

    安装依赖

    create-react-app ./
    yarn add antd
    yarn add less less-loader
    yarn add babel-plugin-import
    yarn add antd-theme-generator
    # 暴露webpack配置
    npm run eject
    

    需要修改 和 创建的文件

    1. config/webpack.config.js
    2. color.js
    3. src/styles/vars.less
    4. src/styles/main.less
    5. public/index.html
    6. package.json
      文件目录

    修改public/index.html

    <body>
        <!--
            这个link的路径,就是下面colors.js配置的outputFilePath路径
        -->
        <link rel="stylesheet/less" type="text/css" href="/color.less" />
        <script>
            window.less = {
                async: false,
                env: 'production'
            };
        </script>
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.2/less.min.js"></script>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
    </body>
    

    创建color.js(根目录)

    const path = require('path');
    const { generateTheme } = require('antd-theme-generator');
    
    const options = {
        // 这个就是样式根目录,就是这么定的,当然路径都可以改
        stylesDir: path.join(__dirname, './src/styles'),
        //  antd包目录
        antDir: path.join(__dirname, './node_modules/antd'),
        // less变量文件
        varFile: path.join(__dirname, './src/styles/vars.less'),
        // 主样式包,实际上可以在这里写一些公共样式
        mainLessFile: path.join(__dirname, './src/styles/main.less'),
        // 主题变量
        themeVariables: [
            '@primary-color',
            '@secondary-color',
            '@text-color',
            '@text-color-secondary',
            '@heading-color',
            '@layout-body-background',
            '@btn-primary-bg',
            '@layout-header-background',
            '@border-color-base',
            '@white'
        ],
        // 样式执行的页面
        indexFileName: 'index.html',
        // 主题样式生成的包文件以及目录
        outputFilePath: path.join(__dirname, './public/color.less'),
        customColorRegexArray: [/^fade\(.*\)$/]
    }
    
    generateTheme(options).then(less => {
      console.log('Theme generated successfully');
    })
    .catch(error => {
        console.log('Error', error);
    });
    

    配置package.json

    "scripts": {
        "start": "npm color && node scripts/start",
        "build": "node scripts/build",
        "color": "node color.js",
        "test": "node scripts/test.js --env=jsdom"
    },
    

    创建vars.lessmain.less

    // `vars.less`
    // 主要定义所有的要改变的颜色变量
    // 先引入antd的less变量
    @import "~antd/lib/style/themes/default.less";
    // 下面的变量就是上面colors.js注册的变量,一一对应,下面的会将antd同名的变量进行覆盖
    @secondry-color: #dddddd;
    @primary-color: #00375B;
    @text-color: #000000;
    @text-color-secondary: #eb2f96;
    @heading-color: #ccccdd;
    @secondary-color: fade(@primary-color, 20%);
    @layout-header-background: #9e8989;
    @btn-primary-bg: #397dcc;
    @bg-color: #dddddd;
    @border-color-base: #ff0000;
    @white: #eeeeee;
    // 下面这个自定义变量挺重要的,想要其它less文件支持主题更换,必须使用css自定义变量,下面会讲
    :root{
        --primary: @primary-color;
    }
    
    // main.less
    // 可以定义一些全局的样式,因为之前colors.js中有引入这个文件,所以这个文件应该是必须的。
    // 有两种方法:引入vars.less的变量,或者使用css的自定义属性
    // 下面就是常规的less写法
    @import "./vars.less";
    .btn {
      color: @primary-color;
    }
    // 下面是css的写法,对这个写法,就是上面vars.less中定义的css变量--primary: @primary-color;
    .btn {
      color: var(--primary)
    }
    

    其他less文件支持局部样式和动态换色

    看下面的例子

    1. 脚手架已经支持局部样式的功能,只要文件的命名格式必须为:name.module.less就行
    2. 文件内部只能通过css自定义变量写法,不支持引入vars.less来动态改变颜色
    3. 如要修改ant的组件样式,需要放在:global()中。不然优先级会低于ant的样式
    // 例:app.module.less
    .text {
        color: var(--primary)
        :global(.ant-menu) {
            border-bottom: 0;
        }
    }
    
    // 例:App.js
    // 点击button动态改变颜色,
    // 之前在main.less中定义了全局样式 .btn
    // 还有上面的app.module.less,注意引入的方法,appless.text
    import React from 'react';
    import { Button } from 'antd'
    import appless from './app.module.less';
    
    const change = () => {
        // 通过点击按钮,将primary-color改成了红色
      window.less.modifyVars({
        '@primary-color': '#cc0000'
      }).then((e) => {
        console.log('切换成功')
      }).catch((e) => {
        console.log(e)
      })
    }
    
    function App() {
      return (
        <div className="App">
          <div className={appless.text}>局部样式,支持动态主题</div>
          <Button className='btn' type="primary" onClick={change}>点击改变主题</Button>
        </div>
      );
    }
    
    export default App;
    
    <!-- 
        这是页面渲染的最终效果
     -->
    <div class="App">
        <div class="app_text__M697S">局部样式,支持动态主题</div>
        <button type="button" class="ant-btn btn ant-btn-primary"ant-click-animating-without-extra-node="false">
            <span>点击改变主题</span>
        </button>
    </div>
    

    webpack.config.js 配置

    // style files regexes
    const cssRegex = /\.css$/;
    const cssModuleRegex = /\.module\.css$/;
    const lessRegex = /\.less$/;
    const lessModuleRegex = /\.module\.less$/;
    const sassRegex = /\.(scss|sass)$/;
    const sassModuleRegex = /\.module\.(scss|sass)$/;
    
    
    const getStyleLoaders = (cssOptions, preProcessor) => {
        const loaders = ['/*********/'].filter(Boolean);
        if (preProcessor) {
            loaders.push(
                {
                    loader: require.resolve('resolve-url-loader'),
                    options: {
                        sourceMap: isEnvProduction && shouldUseSourceMap,
                    },
                },
                {
                    loader: require.resolve(preProcessor),
                    options: {
                        sourceMap: true,
                        javascriptEnabled: preProcessor === 'less-loader',
                        modifyVars: preProcessor === 'less-loader' ? getLessVars(path.join(__dirname, '../src/styles/vars.less')) : false,
                    },
                }
            );
        }
        return loaders;
    };
    
    // less-loader配置
    {
        test: lessRegex,
        exclude: lessModuleRegex,
        use: getStyleLoaders({
            importLoaders: 3,
            sourceMap: isEnvProduction && shouldUseSourceMap,
        },
        'less-loader'),
        sideEffects: true,
    },
    {
        test: lessModuleRegex,
        use: getStyleLoaders({
            importLoaders: 3,
            sourceMap: isEnvProduction && shouldUseSourceMap,
            modules: {
                getLocalIdent: getCSSModuleLocalIdent,
            },
        },
        'less-loader'),
    },
    
    
    // babel-loader 对antd进行按需加载配置
    {
        test: /\.(js|mjs|jsx|ts|tsx)$/,
        include: paths.appSrc,
        loader: require.resolve('babel-loader'),
        options: {
            customize: require.resolve(
                'babel-preset-react-app/webpack-overrides'
            ),
            
            plugins: [
                [
                    require.resolve('babel-plugin-named-asset-import'),
                    {
                        loaderMap: {
                            svg: {
                                ReactComponent:
                                    '@svgr/webpack?-svgo,+titleProp,+ref![path]',
                            },
                        },
                    },
                ],
                [
                    "import",
                    {
                        libraryName: "antd",
                        libraryDirectory: "es",
                        style: true
                    },
                ]
            ],
            // This is a feature of `babel-loader` for webpack (not Babel itself).
            // It enables caching results in ./node_modules/.cache/babel-loader/
            // directory for faster rebuilds.
            cacheDirectory: true,
            // See #6846 for context on why cacheCompression is disabled
            cacheCompression: false,
            compact: isEnvProduction,
        },
    },
    

    总共配置就这些,稍微麻烦点,但是能实现动态切换主题,也是蛮赞的。加油

    相关文章

      网友评论

          本文标题:react + antd 动态切换主题 + 局部样式

          本文链接:https://www.haomeiwen.com/subject/lwkakhtx.html