前言
项目需求,需要将后端传过来的一段JSON字符串,解析成一行或多行的格式,并将key和value以不同颜色进行表示。同时为了以后可能的复用,要将他抽象出来。
效果
填写部分(这里没放代码)
展示部分(单行和多行)
实现
import React, { FC } from 'react';
interface JSONRenderProps {
json: object;
keyColor?: string;
valueColor?: string;
isOneline?: boolean;
}
const JSONRender: FC<JSONRenderProps> = (props: JSONRenderProps) => {
const { json, keyColor = '#f9303c', valueColor = '#0070ff', isOneline = true } = props;
const renderJsonOnline = (json: any): any => {
if (typeof json !== 'object' || json === null) {
return <span style={{ color: valueColor }}>{JSON.stringify(json)}</span>;
}
const children = [];
Object.keys(json).forEach((key) => {
const value = json[key];
children.push(
<span key={`${key}_key`} style={{ color: keyColor }}>
"{key}":{' '}
</span>,
<span key={`${key}_value`}>{renderJsonOnline(value)}</span>,
', ',
);
});
children.pop();
return (
<span>
{'{ '}
{children}
{' }'}
</span>
);
};
const renderJsonMultiLine = (json: any, indent = 1): any => {
if (typeof json !== 'object' || json === null) {
return <span style={{ color: valueColor }}>{JSON.stringify(json)}</span>;
}
const children = [];
const { length } = Object.keys(json);
Object.keys(json).forEach((key, index) => {
const value = json[key];
children.push(
<div key={key} style={{ paddingLeft: `${indent * 20}px` }}>
<span style={{ color: keyColor }}>"{key}": </span>
{typeof value === 'object' ? (
renderJsonMultiLine(value, indent + 1)
) : (
<span style={{ color: valueColor }}>{JSON.stringify(value)}</span>
)}
{index === length - 1 ? '' : ','}
</div>,
);
});
return (
<span style={indent === 1 ? { fontSize: '12px' } : {}}>
{'{ '}
{children}
{' }'}
</span>
);
};
return <>{isOneline ? renderJsonOnline(json) : renderJsonMultiLine(json)}</>;
};
export default JSONRender;
接下来分别介绍
单行渲染
const { json, keyColor = '#f9303c', valueColor = '#0070ff', isOneline = true } = props;
const renderJsonOnline = (json: any): any => {
if (typeof json !== 'object' || json === null) {
return <span style={{ color: valueColor }}>{JSON.stringify(json)}</span>;
}
const children = [];
Object.keys(json).forEach((key) => {
const value = json[key];
children.push(
<span key={`${key}_key`} style={{ color: keyColor }}>
"{key}":{' '}
</span>,
<span key={`${key}_value`}>{renderJsonOnline(value)}</span>,
', ',
);
});
children.pop();
return (
<span>
{'{ '}
{children}
{' }'}
</span>
);
};
解释:
考虑到可能会对象里面value仍旧是对象的情况,因此需要用递归的方式来实现。
首先先写停止条件——判断传入的是否不为对象或者为null,如果为true,那么就返回stringify(json)后的span(PS:判断json === null的原因是 null也是对象,即 typeof null 为object);
当传入的是对象时,首先用Object.keys(json)获取一个存储所有key的数组,然后forEach遍历,在里面拼接类似于"key": value的格式,其中{’ '}代表一个空格
补充:
- 这里面用chidren.push()和children.pop()的原因:因为这样子递归的话,到最后会多出一个「逗号」,因此通过children.pop()方法删除最后一个元素即「逗号」。也有其他方法可以实现,比如用if判断
const children = [];
const keys = Object.keys(json);
keys.forEach((key, index) => {
const value = json[key];
children.push(
<span key={`${key}_key`} style={{ color: keyColor }}>
"{key}":{' '}
</span>,
<span key={`${key}_value`}>{renderJsonOnline(value)}</span>,
);
if (index < keys.length - 1) {
children.push(', ');
}
});
- 为什么最后可以直接return children数组:React 允许在 JSX 中插入数组,因为它会自动将数组中的每个元素渲染为独立的子元素。这使得我们可以非常方便地处理动态生成的内容。
- 为什么是一行:因为用的是span标签,它为内联样式(inline-block)。
- 效果如下
多行渲染
const renderJsonMultiLine = (json: any, indent = 1): any => {
if (typeof json !== 'object' || json === null) {
return <span style={{ color: valueColor }}>{JSON.stringify(json)}</span>;
}
const children = [];
const { length } = Object.keys(json);
Object.keys(json).forEach((key, index) => {
const value = json[key];
children.push(
<div key={key} style={{ paddingLeft: `${indent * 20}px` }}>
<span style={{ color: keyColor }}>"{key}": </span>
{typeof value === 'object' ? (
renderJsonMultiLine(value, indent + 1)
) : (
<span style={{ color: valueColor }}>{JSON.stringify(value)}</span>
)}
{index === length - 1 ? '' : ','}
</div>,
);
});
return (
<span style={indent === 1 ? { fontSize: '12px' } : {}}>
{'{ '}
{children}
{' }'}
</span>
);
};
解释:
- 多行渲染同样是用递归的方式进行渲染,与单行的区别在于,用了div使其为多行,另外多了个indent来控制缩进。当有嵌套的时候(typeof value === ‘object’),递归调用,并让缩进值+1
- {index === length - 1 ? ‘’ : ‘,’}的作用:和单行渲染里面提到的两个方法一样,就是用来增加逗号,并且当是最后一个元素的时候,就不添加逗号。
- 效果如下
封装
return <>{isOneline ? renderJsonOnline(json) : renderJsonMultiLine(json)}</>;
export default JSONRender;
封装并导出JSONRender,后面使用的时候只要传入json和isOneline即可,比如:我想做一个Bubble气泡交互,页面上展示的是一行的样子,当hover的时候,展示多行渲染的JSON
<Bubble
content={
<div style={{ maxHeight: 400, minHeight: 40, minWidth: 100, overflow: 'auto' }}>
{JSONRender({ json, isOneline: false })}
</div>
}
trigger="hover"
placement="top-start"
>
<span>{JSONRender({ json })}</span>
</Bubble>
总结
记录下这个主要是因为封装的还是比较好的,记录下来一是分享给大家,二是自己之后可以消化一下用于秋招。