react + antd 动态切换主题 + 局部样式
2020-03-03 本文已影响0人
HopeLii
看了antd
官网的支持自定义主题,但是不支持动态切换。然后看到antd-pro
支持动态切换主题,但要配合UmiJS
。嗯......
后面又搜到了两个插件(其实就是同一个 -_- )
demo
本文的动态主题实现,就是按照第一个插件来做的,配置过程中,也有坑,因此整理了笔记。
- 原理:通过
less.js
来进行modifyVars
操作 - 插件中提供的案例通过
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
需要修改 和 创建的文件
config/webpack.config.js
color.js
src/styles/vars.less
src/styles/main.less
public/index.html
-
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.less
和main.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
文件支持局部样式和动态换色
看下面的例子
- 脚手架已经支持局部样式的功能,只要文件的命名格式必须为:
name.module.less
就行 - 文件内部只能通过
css
自定义变量写法,不支持引入vars.less
来动态改变颜色 - 如要修改
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,
},
},
总共配置就这些,稍微麻烦点,但是能实现动态切换主题,也是蛮赞的。加油