最近打算写一个简单的全栈项目,在写API文档界面的时候遇到了一个小问题,就是如何显示将服务器返回的示例代码转换成有格式的代码块。
如果是通过 JSON 进行转换的话 ,那么得到的是一个无格式的JSON字符串数据,不符合美观。
直接看效果可能更好理解一些。
假如:
现在有这么个对象:
reParam: { code: 10001, data: { age: 1, name: 'test name', address: 'China', data: { age: 1, name: 'test name', address: 'China', data: { age: 1, name: 'test name', address: 'China', data: { age: 1, name: 'test name', address: 'China', } } } }, message: '获取数据成功' }
如果是通过JSON进行转换将得到这样的结果:
{"code":10001,"data":{"age":1,"name":"test name","address":"China","data":{"age":1,"name":"test name","address":"China","data":{"age":1,"name":"test name","address":"China","data":{"age":1,"name":"test name","address":"China"}}}},"message":"获取数据成功"}
可以看到结果是一行文本串,这完全不符合之前的预期,可阅读性很差,预期的效果是这样:
{ "code": 10001, "data": { "age": 1, "name": "test name", "address": "China", "data": { "age": 1, "name": "test name", "address": "China", "data": { "age": 1, "name": "test name", "address": "China", "data": { "age": 1, "name": "test name", "address": "China", } } } } "message": "获取数据成功", }
用截图来看下:
效果大体上就是这样的,将一个对象这样格式化的展现出来。
应该还是有更好的方法,我只能想出这么一个笨方法来。
接下来就简单说一下在 JS 里,应该如何去实现这样一个效果。
首先说一下最外层的中括号,这个是最后处理的,所以先放在一旁。
再说一下中间的代码块,可以先将对象简单的拆成这样的一个格式:
reParam: { code: 10001, message: '获取数据成功' }
这个方法的难点无非就是不知道这个对象镶嵌了多少层,那就先看看只是一层的时候怎么写。
每一行的内容都是一个 P 标签,所以先写一个方法。
makeADOM(content) { return `<p>${content}</p>` }
这个方法会返回一个包含着对应内容的标签。
然后开始业务代码编写。
toHTML(obj) {for(let key in obj) {
let dom = ''; // 拿到每一项的 value let value = obj[key]; // 分析这个 value 是数字还是字符串,数字不加引号 let _value = typeof value === 'string' ? `"${value}"` : `${value}`; // 将生成 "key": value 格式的内容 dom += makeADOM(`"${key}": ${_value},`); } return dom; }
现在把上面那个简单的对象传进去,看一下效果:
"code": 10001,
"message": "获取数据成功",
对象的结构只有这么一层时很简单,但是如果对象中包含着不知道有多少层对象的时候,那就没这么简单了。
那么接下来修改刚才的函数 toHTML:
toHTML(obj, recursive) {
let dom = ``; for(let key in obj) { // 缓存当前项 let value = obj[key]; // 如果这一项是一个对象,就递归处理 if(typeof value === 'object') { // 先生成一个 key: { 这种格式的行 dom += makeADOM(`"${key}": {`); // 调用递归处理 dom += toHTML(value, true); } else { // 判断是否需要加引号 let _value = typeof value === 'string' ? `"${value}"` : `${value}`; // 生成该行的内容 dom += makeADOM(`"${key}": ${_value},`); } } // 只在递归的时候加回括号,因为只有在处理对象内部的对象的时候才需要{} // 注意递归开始前我们是加了一个 key: { 的行,所以在这里要闭合 if(recursive) { dom += makeADOM(`}`) }
return dom; }
来说一下函数里新增的形参:
recursive 用来判断当前函数体执行的代码是否是对象内镶嵌的层
这样代码基本上就写完了。
但是还有一个格式上的问题。
就是每行前面其实是有着长短不一的空格的,我们现在处理完的结果实际上是这样的:
"code": 10001,
"data": {
"age": 1,
"name": "test name",
"address": "China",
"data": {
"age": 1,
"name": "test name",
"address": "China",
}
}
"message": "获取数据成功",
镶嵌的层越多,阅读体验就会越差,所以我们期望的是这样的:
"code": 10001, "data": { "age": 1, "name": "test name", "address": "China", "data": { "age": 1, "name": "test name", "address": "China", } } "message": "获取数据成功",
所以我们就要给每一行添加空格了,先来写一个添加空格的方法
makeHtmlBlacks(num) { let str = ''; for(let i = 0; i < num; i++) { str += ` ` } return str; }
然后再把 toHTML 函数改巴改巴
toHTML(obj, index = 1, recursive) {for(let key in obj) {
let dom = ''; // 缓存当前项 let value = obj[key]; // 如果这一项是一个对象,就递归处理 if(typeof value === 'object') { // 先生成一个 key: { 这种格式的行 dom += makeADOM(makeHtmlBlacks(index + 1) + `"${key}": {`); // 调用递归处理 dom += toHTML(value, index + 1, true); } else { // 判断是否需要加引号 let _value = typeof value === 'string' ? `"${value}"` : `${value}`; // 生成该行的内容 dom += makeADOM(makeHtmlBlacks(index + 1) + `"${key}": ${_value},`); } } // 只在递归的时候加回括号,因为只有在处理对象内部的对象的时候才需要{} // 注意递归开始前我们是加了一个 key: { 的行,所以在这里要闭合 if(recursive) { dom += makeADOM(makeHtmlBlacks(index) + `}`) }
return dom; }
新增了一个 index 的形参,这个形参是来记录当前深度的,每一个深度我在 makeHtmlBlacks 这个函数里设定的是两个空格,根据深度生成对应数量的空格。
给对象多加几层,再看下效果:
"code": 10001,
"data": {
"age": 1,
"name": "test name",
"address": "China",
"data": {
"age": 1,
"name": "test name",
"address": "China",
"data": {
"age": 1,
"name": "test name",
"address": "China",
"data": {
"age": 1,
"name": "test name",
"address": "China",
}
}
}
}
"message": "获取数据成功",
大致上的效果就是这样了,再把最后一个细节处理一下,就是在外层加一个括号。
toHTML(obj, index = 1, recursive) {
let dom = ''; // 只在非递归的情况下添加 { , 也就是说只会添加一次 if(!recursive) dom += makeADOM(`{`); for(let key in obj) { // 缓存当前项 let value = obj[key]; // 如果这一项是一个对象,就递归处理 if(typeof value === 'object') { // 先生成一个 key: { 这种格式的行 dom += makeADOM(makeHtmlBlacks(index + 1) + `"${key}": {`); // 调用递归处理 dom += toHTML(value, index + 1, true); } else { // 判断是否需要加引号 let _value = typeof value === 'string' ? `"${value}"` : `${value}`; // 生成该行的内容 dom += makeADOM(makeHtmlBlacks(index + 1) + `"${key}": ${_value},`); } } // 只在递归的时候加回括号,因为只有在处理对象内部的对象的时候才需要{} // 注意递归开始前我们是加了一个 key: { 的行,所以在这里要闭合 if(recursive) { dom += makeADOM(makeHtmlBlacks(index) + `}`) } else { // 只在非递归的情况下添加 } , 也就是说只会添加一次 dom += makeADOM(`}`) }
return dom; }
然后再来看一下成型的效果:
{ "code": 10001, "data": { "age": 1, "name": "test name", "address": "China", "data": { "age": 1, "name": "test name", "address": "China", "data": { "age": 1, "name": "test name", "address": "China", "data": { "age": 1, "name": "test name", "address": "China", } } } } "message": "获取数据成功", }
可以看到展示的代码用 {} 包裹了起来。
总结:
方法其实挺简单的,先实现对象只有一层时候的方法,然后在此基础上递归调用此方法处理更深的嵌套层。
其实还可以再DIV一下,在 makeADOM 的函数里做一些改变,比如 key 和 value 可以用盒子包起来,实现一些自定义文本颜色等效果。
在这里就不过多阐述了。
PS:优化的地方:
1、如果类型是个数组
2、同级层过多时用省略号填充
let total = 0; let per = null;
toHTML(obj, index = 1, recursive, _prefixes, parent) {
let dom = '';
if(!recursive) dom += `<p>{</p>`; for(let key in obj) { let value = obj[key]; if(typeof value === 'object') { if(pre === parent) { total++; if(total > 3) { dom += makeADOM(makeHtmlBlacks(index + 1) + `......`); break; } } else { pre = parent; total = 1; } const prefixes = value instanceof Array ? "[]" : "{}"; if(_prefixes === "[]") { dom += makeADOM(makeHtmlBlacks(index + 1) + `${prefixes[0]}`); dom += toHTML(value, index + 2, true, prefixes, value); } else { dom += makeADOM(makeHtmlBlacks(index) + `"${key}": ${prefixes[0]}`); dom += toHTML(value, index + 1, true, prefixes, value); } } else { let _value = typeof value === 'string' ? `"${value}"` : `${value}`; dom += makeADOM(makeHtmlBlacks(index) + `"${key}": ${_value},`); } } if(recursive) { dom += makeADOM(makeHtmlBlacks(index - 1) + _prefixes[1] + ',') } else { dom += `<p>}</p>` }
return dom; }
因为代码是我从 Vue 项目中拷贝出来的,本来方法前面都有 this. ,我手动删掉的,如果发现代码中有 this.xxx 直接忽略就行,如果发现代码错误请联系我修改。
PS:
数组中的值不添加属性名:
let _value = typeof value === 'string' ? `"${value}"` : `${value}`; if(parent instanceof Array) { dom += makeADOM(makeHtmlBlacks(index) + `${_value},`); } else { dom += makeADOM(makeHtmlBlacks(index) + `"${key}": ${_value},`); }
有新的内容会继续补充。