美文网首页
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