前言
antd 5.0 正式发布已经有一段时间了,发布当天,一心二用的看完直播。很喜欢整个设计,有种简约快乐工作的感觉,某些功能设计初衷也给了我一些启发。
antd 5.0 提供的主题定制挺酷炫的,加之我最近对「CSS-in-JS」很感兴趣。于是迫不及待的打开了它的源码,准备研究一番 。
我大部分情况下都是通过碎片化的时间来研究技术,所以时间合理配置和任务合理分块,一直是我常采用的方式。加上对源码阅读的经验不足,所以此次的阅读之旅,我将详细记录阅读前的思考、阅读规划以及收获,并将破冰心得总结之后分享出来。
文章速读
阅读文章,可以有以下收获:
从 demo 开始
前面有一篇源码总结的经验,对于大型的源码阅读,可以从demo开始。
接下来,我将从antd的demo开启阅读之旅。
混合主题
默认主题样式
// docs/examples/components/theme.tsx
// 默认主题样式
const defaultDesignToken: DesignToken = {
primaryColor: '#1890ff',
textColor: '#333333',
reverseTextColor: '#FFFFFF',
componentBackgroundColor: '#FFFFFF',
borderRadius: 2,
borderColor: 'black',
borderWidth: 1,
};
这个默认主题样式,通过变量名大致能猜出来各自代表什么。但是具体应用在哪些元素上,还需要通过使用设置才能确定。
按钮组件
// docs/examples/components/Button.tsx
// 按钮组件
import React from 'react';
import classNames from 'classnames';
import { useToken } from './theme';
import type { DerivativeToken } from './theme';
import { useStyleRegister } from '../../../src/';
import type { CSSInterpolation, CSSObject } from '../../../src/';
// 通用框架
const genSharedButtonStyle = (
prefixCls: string,
token: DerivativeToken,
): CSSInterpolation => ({
[`.${prefixCls}`]: {
borderColor: token.borderColor, // 边框颜色
borderWidth: token.borderWidth, // 边框宽度
borderRadius: token.borderRadius, // 边框圆角
cursor: 'pointer',
transition: 'background 0.3s',
},
});
// 实心底色样式
// 返回数组,第一个元素是通用样式,第二个元素是自定义样式,需要调用者传入
const genSolidButtonStyle = (
prefixCls: string,
token: DerivativeToken,
postFn: () => CSSObject,
): CSSInterpolation => [
genSharedButtonStyle(prefixCls, token),
{
[`.${prefixCls}`]: {
...postFn(),
},
},
];
// 默认样式
const genDefaultButtonStyle = (
prefixCls: string,
token: DerivativeToken,
): CSSInterpolation => {
genSolidButtonStyle(prefixCls, token, () => ({
backgroundColor: token.componentBackgroundColor, // 默认样式的背景颜色
color: token.textColor, // 默认样式的字体颜色
'&:hover': {
borderColor: token.primaryColor, // 默认样式的经过时边框颜色
color: token.primaryColor, // 默认样式的经过时字体颜色
},
}));
};
// 主色样式
const genPrimaryButtonStyle = (
prefixCls: string,
token: DerivativeToken,
): CSSInterpolation =>
genSolidButtonStyle(prefixCls, token, () => ({
backgroundColor: token.primaryColor, // 主色样式的背景颜色
border: `${token.borderWidth}px solid ${token.primaryColor}`, // 主色样式的边框样式
color: token.reverseTextColor, // 主色样式的字体样式
'&:hover': {
backgroundColor: token.primaryColorDisabled, // 主色样式的经过时背景颜色
},
}));
// 透明按钮
const genGhostButtonStyle = (
prefixCls: string,
token: DerivativeToken,
): CSSInterpolation => [
genSharedButtonStyle(prefixCls, token),
{
[`.${prefixCls}`]: {
backgroundColor: 'transparent', // 透明样式的背景颜色
color: token.primaryColor, // 透明样式的字体颜色
border: `${token.borderWidth}px solid ${token.primaryColor}`, // 透明样式的边框颜色
'&:hover': {
borderColor: token.primaryColor, // 透明样式的经过时背景颜色
color: token.primaryColor,
},
},
},
];
interface ButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {
type?: 'primary' | 'ghost' | 'dashed' | 'link' | 'text' | 'default';
}
const Button = ({ className, type, ...restProps }: ButtonProps) => {
const prefixCls = 'ant-btn';
// 【自定义】制造样式
const [theme, token, hashId] = useToken();
// default 添加默认样式选择器后可以省很多冲突解决问题
const defaultCls = `${prefixCls}-default`;
const primaryCls = `${prefixCls}-primary`;
const ghostCls = `${prefixCls}-ghost`;
// 全局注册,内部会做缓存优化
// 目前是三种类型的样式:默认样式、主色样式、透明样式
const wrapSR = useStyleRegister(
{ theme, token, hashId, path: [prefixCls] },
() => [
genDefaultButtonStyle(defaultCls, token),
genPrimaryButtonStyle(primaryCls, token),
genGhostButtonStyle(ghostCls, token),
],
);
const typeCls =
(
{
primary: primaryCls,
ghost: ghostCls,
} as any
)[type as any] || defaultCls;
return wrapSR(
<button
className={classNames(prefixCls, typeCls, hashId, className)}
{...restProps}
/>,
);
};
export default Button;
按钮组件中,主要包含三种主题,样式做了通用设置。从目前的代码看 genGhostButtonStyle 的设置其实和另外两个是一样的。
而通用样式时的取值来自 useToken,useToken则是采用组件树间进行数据传递的方式。
组件间传递样式
// docs/examples/components/theme.tsx
// 创建一个 Context 对象 ThemeContext
export const ThemeContext = React.createContext(createTheme(derivative));
// 创建一个 Context 对象 DesignTokenContext
export const DesignTokenContext = React.createContext<{
token?: Partial<DesignToken>;
hashed?: string | boolean;
}>({
token: defaultDesignToken,
});
/**
* 创建默认样式,并缓存 token
* @returns 包含 theme, token, hashed 的数组对象
*/
export function useToken(): [Theme<any, any>, DerivativeToken, string] {
// 订阅 DesignTokenContext
// 将此处的 token 重命名为 rootDesignToken,并设置默认值 defaultDesignToken
const { token: rootDesignToken = defaultDesignToken, hashed } =
React.useContext(DesignTokenContext);
// 订阅 ThemeContext
const theme = React.useContext(ThemeContext);
// 将 theme 派生的 token 缓存为全局共享 token
// 实际 token 的取值
const [token, hashId] = useCacheToken<DerivativeToken, DesignToken>(
theme,
[defaultDesignToken, rootDesignToken],
{
salt: typeof hashed === 'string' ? hashed : '',
},
);
return [theme, token, hashed ? hashId : ''];
}
这里有一处处理需要注意
标签:5.0,const,样式,prefixCls,酷炫,theme,ant,token,import From: https://blog.51cto.com/u_15838863/8702093