theme: fancy
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
前言
最近来了一个需求,用js比对两个json字符串之间的差异,并用明显的颜色标示出来。一开始这个需求领导是丢给后端的,后端试了一圈之后,表示不太行于是这锅又甩了回来。行吧,心痛的接下,结果找了一圈都是比对json文件的编辑器,网上大部分给出的办法就是使用diffJs加上自渲染去展示。然而,自渲染还是有一定难度的,坑大好磕。
难点:
- 要格式化json字符串,该换行的换行,该留空格的要留空格。
- json深度越大,层级越多越难,很难自己通过js去判断两个json的区别。
- 要注意标红的行和字要如何处理
对比两种diffJs方法
网上找到两种diff工具json-diff和js-diff,简单尝试了下,这两种js的处理的数据有一些区别:
var json1 = { label: '11', value: '22', arr: [1, 2, 3], obj: { label: '11' } }
var json2 = { label: '112', value: '22', arr: [1, 4, 5], list: [1, 1] }
js-diff比对获取到的数据:
var jsDiff = [
{
"count": 3,
"value": "{\n \"arr\": [\n 1,\n"
},
{
"count": 2,
"removed": true,
"value": " 2,\n 3\n"
},
{
"count": 2,
"added": true,
"value": " 4,\n 5\n"
},
{
"count": 1,
"value": " ],\n"
},
{
"count": 4,
"removed": true,
"value": " \"label\": \"11\",\n \"obj\": {\n \"label\": \"11\"\n },\n"
},
{
"count": 5,
"added": true,
"value": " \"label\": \"112\",\n \"list\": [\n 1,\n 1\n ],\n"
},
{
"count": 2,
"value": " \"value\": \"22\"\n}"
}
]
json-diff比对获取的数据:
var jsonDiff = {
"obj__deleted": {
"label": "11"
},
"list__added": [
1,
1
],
"label": {
"__old": "11",
"__new": "112"
},
"arr": [
[
" "
],
[
"-",
2
],
[
"-",
3
],
[
"+",
4
],
[
"+",
5
]
]
};
很明显的可以看出,js-diff比较符合要求,即带有换行,而且比对出来的信息比json-diff完整。
- js-diff只有三种状态,removed、added、value。removed表示json1移除内容,added表示json2新增内容,value表示值没有修改;
- 获取removed和added两种状态的value值,可以得到完整的json1和json2;
json-diff就比较复杂了,而且返回的信息中不包括匹配的信息。
- 根据不同对象有不同的返回结果。
- 数组对象返回数组,数组内每个数组有一个或两个长度[string[,string|object]],第一个字符表示匹配状态,” “为空格时数组没有第二个值,表示这个json中当前索引位置的值没有修改,”-“表示删除值,”+”表示新增值,当状态为’-‘/’+’的时候,第二个索引值返回修改的值;
- 其他对象返回{}对象,返回的key是json中有修改值的key,key的值中”__old”是修改前的值,”__new”是修改后的值,没有修改的key不返回,新增的key从key__added中获取,删除的key从key__deleted中获取。
- 好复杂啊啊啊
对比之下,最后还是选择js-diff来处理。
使用js-diff渲染html
要点:
- 根据removed和added标记修改的块,同时还要使用jsDiff.diffWordsWithSpace标注修改的具体字符
- 新增行添加样式error-line-add,删除行添加样式error-line-remove
//获取对比后的结果及拼接展示效果
const diffJSON = (json1, json2) => {
// diff之后的结果
const diffResult = jsDiff.diffJson(json1, json2);
let addedList = [];
let removedList = [];
//遍历对比结果,摘出相应的数据
//首先判断的量级为一行
diffResult.forEach((diffObj, index) => {
let content = diffObj.value;
let replaceContent = replaceContentFunc(content);
//该项的下一项
let next = diffResult[index + 1];
let nextReplceContent = next && replaceContentFunc(next.value);
if (diffObj.removed) {
//需要展示在历史结果位置的数据
let dealedContent = '';
if (diffObj.removeContent) {
dealedContent = diffObj.removeContent;
} else {
//判断下一个项
if (next && next.added) {
//说明两个项之前是对应的关系
//判断在不同的这行中,具体到字段的不同位置
let testDiffList = jsDiff.diffWordsWithSpace(replaceContent, nextReplceContent);
let { addContent, removeContent } = diffText(testDiffList);
next.addContent = addContent;
dealedContent = removeContent;
} else {
dealedContent =
'<div class="error-line-remove" style="color:red"">' +
replaceContent.replace(/\s/gm, ' ') +
'</div>';
}
removedList.push(dealedContent);
}
} else if (diffObj.added) {
//需要展示在当前结果位置的数据
let dealedContent = '';
if (diffObj.addContent) {
dealedContent = diffObj.addContent;
} else {
//判断下一个项
if (next && next.removed) {
//说明两个项之前是对应的关系
//判断在不同的这行中,具体到字段的不同位置
let testDiffList = jsDiff.diffWordsWithSpace(replaceContent, nextReplceContent);
let { addContent, removeContent } = diffText(testDiffList);
next.removeContent = removeContent;
dealedContent = addContent;
} else {
dealedContent =
'<div class="error-line-add" style="color:red">' +
replaceContent.replace(/\s/gm, ' ') +
'</div>';
}
}
addedList.push(dealedContent);
} else {
//没有改动的部分
replaceContent = replaceContent.replace(/\s/gm, ' ');
addedList.push('<div>' + replaceContent + '</div>');
removedList.push('<div>' + replaceContent + '</div>');
}
});
let addedHtml = addedList.join('');
let removedHtml = removedList.join('');
return {
addedHtml,
removedHtml,
};
};
//对比行内容差异并且拼接结果
const diffText = result => {
// json2 当前数据
let addList = [];
// json1 原始数据
let removeList = [];
result.forEach(item => {
let value = item.value.replace(/\s/gm, ' ');
if (item.added) {
addList.push('<span style="color:red">' + value + '</span>');
} else if (item.removed) {
removeList.push('<span style="color:red">' + value + '</span>');
} else {
removeList.push('<span>' + value + '</span>');
addList.push('<span>' + value + '</span>');
}
});
return {
addContent: `<div class="error-line-add">${addList.join('')}</div>`,
removeContent: `<div class="error-line-remove">${removeList.join('')}</div>`,
};
};
//处理换行和空格
const replaceContentFunc = content => {
let text = content;
if (content.indexOf('\n') >= 0) {
// 换行替换为<br/>
// 空格替换为 
const reg1 = new RegExp('\n', 'g');
text = text.replace(reg1, '<br/>');
}
return text;
};
.error-line-remove {
background-color: rgb(248, 216, 216);
position: relative;
&::before {
content: '-';
position: absolute;
left: 5px;
color: black;
line-height: 19.5px;
}
}
.error-line-add {
background-color: rgb(226, 248, 216);
position: relative;
&::before {
content: '+';
position: absolute;
left: 5px;
color: black;
line-height: 19.5px;
}
}
最后处理的样式如下:
最后,这篇还是有参考代码的,json-diff没有…爱学习的我顺便也把json-diff的渲染也给写出来了,用递归写的,给我绕的(脑壳不够用),下一篇我会发出来,大家自己也可以尝试下。ORZ…拜拜
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容