不能不说,写这个的时候,真的好饿!
描述:在nextjs中对antd的亮白和暗黑主题切换实践。
在线预览:https://imbf.cc/demo/theme 18:00-7:00是暗黑主题,可以通过Redux DevTools提交action,{type: 'change_theme',value: 'dark/light'}
来切换,或者在预览页面的下拉菜单切换也可以。
代码仓库:https://github.com/imzbf/nextjs-start
预览图
主要用到一个webpack插件antd-theme-webpack-plugin
。下面会一步一步的造~
这个插件是基于使用webpack环境的,暂时没有去看有没有大佬有开发支持vite的插件。在支持服务端渲染的项目中有了实践,那么在单页应用客户端渲染的项目中,就大同小异了。
搭建项目
其中添加redux、舍弃nextjs自身的css使用withCss插件、添加less\sass以及支持模块样式的内容就不谈了,内容很简单,可以直接看代码。
添加主题依赖
yarn add -D antd-theme-webpack-plugin
配置项
1.添加自定义的主题
向styles文件夹中添加vars.less,内容可以像下面:
// This file will contain all varibales, our custom varibales and
//those from Ant Design which we want to override.
@import '~antd/lib/style/themes/default.less';
@primary-color: @green-6;
@select-item-selected-option-color: @primary-color;
@processing-color: @primary-color;
@select-item-selected-bg: @background-color-base;
@secondary-color: @primary-color;
@skeleton-color: @primary-color;
@btn-primary-bg: @primary-color;
:root {
--PC: @primary-color;
}
此内容来自nextjs的demo
2.添加next配置
next.config.js
const AntDesignThemePlugin = require('antd-theme-webpack-plugin');
// antd-theme-generator插件在上面的依赖项目中有,所以不需要安装
const { getLessVars } = require('antd-theme-generator');
// 这一步是读取我们预设的主要的主题变量(是不支持热更新的)
const themeVariables = getLessVars(path.join(__dirname, './src/styles/vars.less'));
// 读取暗黑主题
const darkVars = {
...getLessVars('./node_modules/antd/lib/style/themes/dark.less'),
'@picker-basic-cell-active-with-range-color': 'darken(@primary-color, 20%)'
};
// 亮白主题
const lightVars = {
...getLessVars('./node_modules/antd/lib/style/themes/compact.less')
};
// 这一步即使启动或构建项目时生成对应的主题文件,用于项目中import
// 最好的gitignore该生成的文件,每次自动生成
fs.writeFileSync('./src/config/dark.json', JSON.stringify(darkVars));
fs.writeFileSync('./src/config/light.json', JSON.stringify(lightVars));
fs.writeFileSync('./src/config/theme.json', JSON.stringify(themeVariables));
// 这里就是theme插件的配置项了
const themeOptions = {
stylesDir: path.join(__dirname, './src'),
antDir: path.join(__dirname, './node_modules/antd'),
varFile: path.join(__dirname, './src/styles/vars.less'),
themeVariables: Array.from(
new Set([
...Object.keys(darkVars),
...Object.keys(lightVars),
...Object.keys(themeVariables)
])
),
indexFileName: false,
// 这里需要将生成的less文件放到一个生产环境能够直接通过link获取的地方,所以这里放到public下
outputFilePath: path.join(__dirname, './public/color.less'),
generateOnce: false
};
module.exports = withPlugins([withLess], {
lessLoaderOptions: {
javascriptEnabled: true,
importLoaders: 1
},
webpack: (config, options) => {
// 添加主题切换webpack插件
config.plugins.push(new AntDesignThemePlugin(themeOptions));
}
}
这部分即使最主要的配置内容,后面将开始使用该配置生成的内容了
3.添加切换主题内容
由于nextjs中是没有html文件的,所以我们不能像Nextjs的demo一样直接添加标签引入内容,但是可以稍微变通一下。
这里选择在pages下的_app.tsx动态添加该内容
注意:如果像https://github.com/imzbf/nextjs-start中的源码一样,使用了redux或mobx之类的跨组件状态管理的话,在Provider所在主题可能不能很好的提交action,可以选择在更深层的公共组件中完成,比如Layout中。
_app.tsx
import { useEffect } from 'react';
// 开发阶段导入json,共线上环境直接使用
import darkVars from '../config/dark.json';
import lightVars from '../config/light.json';
// import themeVars from '../config/theme.json';
const Layout = ({ Component, pageProps }) => {
useEffect(() => {
window['less'] = {
async: true,
env: 'production'
};
const script = document.createElement('script');
// 等到less加载完成后执行,免得报错
script.addEventListener('load', () => {
window['less']
.modifyVars(
config.name === 'light' ? lightVars : darkVars
)
.catch((error) => {
console.error(error);
});
});
// 重点,高于2.7.3版本的less在浏览器端运行会抛错,可以自己踩踩看
script.src = 'https://cdn.bootcdn.net/ajax/libs/less.js/2.7.3/less.min.js';
document.body.appendChild(script);
const css = document.createElement('link');
css.href = '/color.less';
css.rel = 'stylesheet/less';
css.type = 'text/css';
document.body.appendChild(css);
}, []);
return (<Component {...pageProps} />);
};
export default Layout;
从代码中可以看出来,实际上切换主题只是运行了window.less.modifyVars
,所以我们只需要将切换主题的下拉框选择时间与其绑定即可
4.在sass中适配
less的变量不要想着直接拿到sass中直接使用(也许有办法),我在博客中采用的方法是最简单的皮肤切换方式:修改外部className,新增dark对应的样式。
如此,在调用window.less.modifyVars
后,可以提交redux action修改对应的state,然后切换外部的class,这算得上是工作量和难度都较低的方法了。
写在末尾
其中的解决方案来自网络,作者只是抽时间将所学拼在了一起。主要的内容还是来自nextjs的github demo中。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。