前言
在定制化的场景中,后端返回给我们一段字符串文本,我们需要对里面的某些内容进行样式的高亮(变颜色,字体大小等)。
比如,后端返回给我们这么一段字符串:
"2022-02-02全天呈现畅通状态,全天拥堵指数:1.06,环比昨日0.3%"
复制代码
现在我们想对字符串文本的 2022-02-02
、畅通
、 1.06
、0.3%
进行不同类型的样式高亮处理。效果如下:
这样的情况,我们该如何处理了?
问题拆分
我们可以后端协商,定义通用的规则,将不同类型的高亮文本进行分类,通过特殊的标记,替换成相应的样式。
方案
在该案例中,我们将高亮文本分为时间、状态、数值、比率,定义特殊的标记。
描述类型:
- 时间 ${date}
- 数值 ${number}
- 比率 ${ratio}
- 状态 ${status}
描述格式:
${xxx}数据${/xxx}
复制代码
示例:
约定后端返回如下的格式实例:
"${date}2022-02-02${/date}全天呈现${status}畅通${/status}状态,全天拥堵指数:${number}1.06${/number},环比昨日${ratio}0.3%${/ratio}"
复制代码
重点:
- 识别特殊标记,并替换标记;
- 将字符串转译成html;
这里我们通过正则表达式匹配特殊标记,通过replace方法替换特殊字符。再使用react的dangerouslySetInnerHTML设置 HTML。
第一版
具体代码如下:
import React from "react";
const formatStringRenderToJsxElement = (value: string) => {
if (value === null || value === undefined) {
return "--";
}
if (typeof value === "string") {
// 时间
const dateStartReg = RegExp("\\${date}", "g");
const dateEndReg = RegExp("\\${/date}", "g");
// 数值
const numberStartReg = RegExp("\\${number}", "g");
const numberEndReg = RegExp("\\${/number}", "g");
// 比率
const ratioStartReg = RegExp("\\${ratio}", "g");
const ratioEndReg = RegExp("\\${/ratio}", "g");
// 状态
const statusStartReg = RegExp("\\${status}", "g");
const statusEndReg = RegExp("\\${/status}", "g");
const result = value
.replace(dateStartReg, '<span style="color: #00bcd4">')
.replace(dateEndReg, "</span>")
.replace(numberStartReg, '<span style="color: #4285ff">')
.replace(numberEndReg, "</span>")
.replace(ratioStartReg, '<span style="color: #ff0000">')
.replace(ratioEndReg, "</span>")
.replace(statusStartReg, '<span style="color: #1fc371">')
.replace(statusEndReg, "</span>");
return <span dangerouslySetInnerHTML={{ __html: result }} style={style}></span>;
}
return undefined;
};
复制代码
第二版
这里我们把代码精简一下,将传入的值抽取,变得通用一些,添加一些类型的判断,防止注入攻击的逻辑等。
具体代码如下:
const formatStringRenderToJsxElement = (
value: string | number,
config: { mark: string; replaceValue: string;}[]
) => {
if (value === null || value === undefined) {
return "--";
}
if (typeof value === "number") {
return value;
}
if (typeof value === "string") {
// 防止注入攻击
let result = value.replace(RegExp("<", "g"), "<").replace(RegExp(">", "g"), ">");
config?.forEach((item) => {
result = result.replace(RegExp(item.mark, "g"), item.replaceValue);
});
return <span dangerouslySetInnerHTML={{ __html: result }}></span>;
}
return value;
};
复制代码
使用示例:
const value = "${date}2022-02-02${/date}全天呈现${status}畅通${/status}状态,全天拥堵指数:${number}1.06${/number},环比昨日${ratio}0.3%${/ratio}";
const config = [
{ mark: "\\${date}", replaceValue: '<span style="color: #9E2323">' },
{ mark: "\\${/date}", replaceValue: "</span>" },
{ mark: "\\${status}", replaceValue: '<span style="color: #F28135">' },
{ mark: "\\${/status}", replaceValue: "</span>" },
{ mark: "\\${number}", replaceValue: '<span style="color: #FEC919">' },
{ mark: "\\${/number}", replaceValue: "</span>" },
{ mark: "\\${ratio}", replaceValue: '<span style="color: #1FC371">' },
{ mark: "\\${/ratio}", replaceValue: "</span>" },
];
formatStringRenderToJsxElement(value, config)
复制代码
总结
再不同的业务场景中,我们可以对特殊标记进行不同的定义,方便扩展,原则上避免使用常用符号,避免因用户输入的数据和标记一样,导致检测逻辑异常。关于代码注入html,这块可能会有XSS(跨站脚本攻击),这里需要额外注意,特殊情况下, 需要做安全检测处理。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END