React 基础组件规范
组件的定义方式以实现最大程度上的可组合为优先。
基础结构
对于一个最小的可组合组件来说,大致结构如下:
interface StepperProps {
current: number;
}
const Stepper: FC<StepperProps> = (props) => {
const { current } = props;
return <div>{current}</div>;
};
组件拆分与组织
对于大型组件需要拆分,namespace 的组织方法已经随着 RSC 兴起被逐步废弃,组件的组织方法只能是这样:
const PreviewHeader: FC = ({ title }) => {
return <div>{title}</div>
}
const PreviewContent: FC = ({ children }) => {
return <div>{children}</div>
}
const PreviewContainer: FC = ({ header, content, footer }) => {
return (
<section>
<header>{header}</div>
<main>{content}</div>
</section>
)
}
通过 slot 的方式来将组件拆分,最大程度上实现可组合。
转发 DOM 属性
尽量暴露足够多的 props,最大程度上保证调用处的方便。
interface InputProps extends ComponentProps<"input"> {
label: string;
}
const Input: FC<InputProps> = forwardRef((props, ref) => {
const { label, ...rest } = props;
return (
<label>
{label}
<input {...props} ref={ref} />
</label>
);
});
样式、布局、结构、功能分离
先阐释概念:
- 样式:决定外观的部分
- 布局:决定位置的部分
- 结构:为组件的组成提供视觉锚定的部分
- 功能:决定组件的行为的部分
比如:
// 决定结构
const PreviewContainer: FC = (props) => {
return (
<section className={classNames("", className)}>
<header>{props.header}</header>
<main>{props.content}</main>
</section>
);
};
// 具体的组件,表达样式
const PreviewButton: FC = (props) => {
return (
<button className={classNames("", className)} {...props}>
{props.children}
</button>
);
};
const AppPreview: FC = (props) => {
return (
<PreviewContainer
header={
<PreviewButton
// 在实际的页面中决定功能
onClick={() => console.log("hello")}
>
hello
</PreviewButton>
}
>
{/* 在此处决定布局 */}
<div className="absolute top-0 left-0 flex flex-col gap-2"></div>
</PreviewContainer>
);
};