代码优雅化进阶学习(二)
实现一行个性化自定义样式文本
需求详情
实现以下的内容:
实现高亮效果
分析设计
难点
若只是实现以上内容,则很简单
但若从具体抽象到一般,则不容易
若要实现一般化,就需要抽象,寻找变与不变,封装变化,使变化的部分灵活,不变的部分稳定。所以需要考虑到以下几个方面:
- 如何抽象特殊文本的位置:特殊文本的位置是变化的,如何确定位置
- 如何抽象特殊文本的样式:特殊文本样式不仅仅是加粗,若未来需求变化,改为了其他特殊样式,该如何拓展
- 如何从业务需求中抽离出来,实现真正的一般化、抽象化
旧设计
设计详情:
将每一行文字分为 高亮文字前,高亮文字,高亮文字后 三种部分,若没有高亮文字,则就是 desc
优点
- 抽象了特殊文字的位置
- 可以实现样式的拓展,不只是可以实现高亮的效果
缺点
- 分得太细,这种数据设计导致后面实现连带变得复杂
- 命名不够抽象,不具有一般性和扩展性,若之后不是展示价格和折扣,则命名失去意义
- 将价格和折扣分开来抽象了,需要提取两者共性,将两者结合来具体抽象
- 抽象的还不够具体,还需再进一步抽象
- 没有从业务需求中抽离出来,单独来看,我们所需要实现的就是 一行文字中有些特殊文本
新设计
设计详情:
- 抽离业务,就变成了一行文字中带有一部分特殊样式的文本
- 文本样式可自定义
- 文本位置可自定义
思路
思路1:占位符,通过占位符来替换文本(但是占位符的位置如何确定)
思路2:
- 传入整行文本
- 传入特殊样式的文本数组
- 抽取共性,形成一个组件
- 实现了更深一层的抽象
- 注意抽象命名
实现
import React, { FC, ReactNode } from 'react';
export interface IAttributeTextProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
/**
* 要展示的整个文本
*/
content?: string;
/**
* 需要特殊样式的文本
*/
attributes?: IAttributeInfo[];
}
export interface IAttributeInfo extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> {
/**
* 文本
*/
text: string;
}
export const AttributeText: FC<IAttributeTextProps> = (props) => {
const { content = '', attributes = [], ...resetProps } = props;
return <div {...resetProps}>{getAttributeText(content, attributes)}</div>;
};
function getAttributeText(content: string, attributes: IAttributeInfo[]) {
const cAttributes = [...attributes];
if (!cAttributes.length) {
return content;
}
let splitText = content;
const attributeText: (ReactNode | string)[] = [];
cAttributes.forEach((item, index) => {
const { text, ...resetProps } = item;
const textIndex = splitText.indexOf(text);
const beforeText = splitText.substring(0, textIndex);
if (beforeText) {
attributeText.push(beforeText);
}
attributeText.push(
<span key={index} {...resetProps}>
{text}
</span>
);
splitText = splitText.substring(textIndex + text.length);
if (index === cAttributes.length - 1 && splitText) {
attributeText.push(splitText);
}
});
return attributeText;
}
使用方法
const medalDesc: IAttributeTextProps[] = [
{
content: '300元红包',
attributes: [
{
text: '300',
style: { fontWeight: '700' }
}
]
},
{
content: '优惠卷50元',
attributes: [
{
text: '50',
style: { color: '#000' }
}
]
}
];
<div>
{medalDesc.map((desc, idx) => (
<AttributeText key={idx} {...desc} />
))}
</div>
前端小菜鸟进阶学习中,欢迎指正!