我给自己写了一个本地 JSON 工具:格式化、对比差异、UI 全自定义实录

举报
yd_294377644 发表于 2026/10/29 09:46:43 2026/10/29
【摘要】 调试接口时常要格式化 JSON、对比两次返回差异,市面工具要么广告多、要么强推云端、要么大文件卡顿。我想要一个安静、本地、能格式化、能高亮差异、能秒开大文件的 JSON 工具,于是自己写了一个。这篇博客记录从需求到落地的全过程:技术选型、格式化、差异对比、UI 定制心得,以及那些自己写一遍才明白的细节。

目录

  1. 缘起:为什么我不再用任何在线 JSON 工具
  2. 需求拆解:从"能格式化"到"能秒懂差异"
  3. 技术选型:一个 HTML 文件就够了
  4. 核心实现一:格式化与语法高亮
  5. 核心实现二:两份 JSON 的差异对比
  6. UI 定制:JSON 工具也是"自己的工作台"
  7. 三个让我卡了一阵的坑
  8. 结尾:开发者工具的"双栏对照"哲学

1. 缘起:为什么我不再用任何在线 JSON 工具

我每天要和 JSON 打交道——接口返回、配置文件、日志片段。常做的事就两件:格式化和对比差异。但市面工具总让我皱眉:

  • 在线工具广告多、加载慢,断网就废;
  • 强推"保存到云端",我贴的可能是带 token 的接口返回;
  • 大文件卡顿,几 MB 的 JSON 直接把页面冻住;
  • 差异对比要么不支持、要么高亮不清晰,看不出"到底哪一层变了"。

我忽然意识到:JSON 处理是最高频的本地操作,凭什么要联网、要登录、要等加载。既然现成工具都在制造摩擦,那就自己写一个。

2. 需求拆解:从"能格式化"到"能秒懂差异"

把"做一个 JSON 工具"拆成可验收清单:

诉求(人话) 拆解后(可验收)
能格式化 粘贴即格式化,缩进可调(2/4/Tab)
能压缩 一键压成单行,去空格换行
能高亮 key/string/number/bool/null 不同色
能对比 左右两份 JSON,逐层高亮增删改
能折叠 大对象可按层级折叠,秒开几 MB
不上传 全程本地,不发起任何网络请求

这张表决定了工具的骨架。功能不多,但每一项都做到位——这是我对所有"自己用的工具"的一贯要求。

3. 技术选型:一个 HTML 文件就够了

我犹豫过要不要上某个 JSON 库,但仔细想需求——浏览器原生 JSON.parse + JSON.stringify 已经足够,差异对比自己写递归更可控:

能力 方案
解析 原生 JSON.parse,带错误定位
格式化 JSON.stringify(obj, null, indent)
高亮 自写 tokenizer + 着色
折叠 渲染成可折叠的树节点
差异 递归对比两棵树,标记 add/del/mod

整个工具就是一个 HTML 文件,双击就能开。没有安装包、没有更新提示、没有后台进程——JSON 全程在内存里,关掉即消失。

心得:选型时先问"我到底需要什么"。本地小工具的最高境界是双击即用、关掉即走。

4. 核心实现一:格式化与语法高亮

格式化本身一行:

const formatted = JSON.stringify(JSON.parse(input), null, indent);

但语法高亮要自己来。最稳的做法是用正则 tokenizer 切分 token,按类型着色:

function highlight(json) {
    return json.replace(
        /("(?:\\.|[^"\\])*"\s*:?)|(\b-?\d+\.?\d*\b)|(\btrue\b|\bfalse\b)|(\bnull\b)/g,
        (m, str, num, bool, nul) => {
            if (str) return `<span class="json-key">${str}</span>`;
            if (num) return `<span class="json-num">${num}</span>`;
            if (bool) return `<span class="json-bool">${bool}</span>`;
            if (nul) return `<span class="json-null">${nul}</span>`;
        }
    );
}

几个让体验"顺手"的细节:

  • 错误定位到行列。JSON.parse 抛错时带 message,但原生不给位置。我用一个轻量 json-parse-even-better-errors 思路——失败时二分定位非法字符,提示"第 N 行第 M 列";
  • 缩进可切换。2 空格 / 4 空格 / Tab 三档,顶部一排切换;
  • 大文件虚拟渲染。几千行的 JSON 不全塞进 DOM,只渲染可视区,滚动时按需补。

心得:JSON 工具的核心指标是"贴进来不卡、看下去不晕"。性能和可读性缺一不可。

5. 核心实现二:两份 JSON 的差异对比

差异对比是这类工具的灵魂。递归比较两棵树,标记四种状态:

function diff(a, b, path = '') {
    if (a === b) return [];                          // 相同
    if (a == null || b == null) return [{path, type: 'mod', from: a, to: b}];
    if (typeof a !== typeof b) return [{path, type: 'mod', from: a, to: b}];

    if (Array.isArray(a) && Array.isArray(b)) {
        return diffArrays(a, b, path);              // 数组按索引比
    }
    if (typeof a === 'object') {
        return diffObjects(a, b, path);             // 对象按键比
    }
    return [{path, type: 'mod', from: a, to: b}];   // 原始值变了
}

diffObjects 里对键做三种判断:

function diffObjects(a, b, path) {
    const result = [];
    for (const k of new Set([...Object.keys(a), ...Object.keys(b)])) {
        if (!(k in a))      result.push({path: `${path}.${k}`, type: 'add', to: b[k]});
        else if (!(k in b)) result.push({path: `${path}.${k}`, type: 'del', from: a[k]});
        else                result.push(...diff(a[k], b[k], `${path}.${k}`));
    }
    return result;
}

渲染时,用路径定位到树节点,add 染绿、del 染红、mod 染黄并显示新旧值。几个让差异"秒懂"的细节:

  • 差异列表单独一栏。不只染色,还列出".user.name: ‘a’ → ‘b’"这种可读条目;
  • 点击差异条目跳转到树中对应节点。大对象里不用自己翻找;
  • 数组按索引比,不按内容比。这是有意的——接口返回的数组顺序通常有意义,按索引比才能发现"第 3 个元素变了"。

心得:差异工具的核心指标是"不用展开整棵树就能看懂哪层变了"。差异列表 + 路径跳转,比纯染色直观十倍。

6. UI 定制:JSON 工具也是"自己的工作台"

本地工具最大的特权,是 UI 完全我说了算。我给自己定了几个商业软件不会这么做的设计选择:

  • 双栏对照:左原始 / 右格式化,或左旧 / 右新(对比模式),顶部一排切换;
  • 等宽字体 + 行号。长 JSON 没行号根本没法定位错误;
  • 暗色为主,跟随系统。长时间看 JSON,暗色更护眼;
  • 错误不弹窗。解析失败时输入框红框 + 下方一行红字提示行列,不打断输入流。

这些设计在商业产品里几乎不可能出现——它们太"个人化"了。但本地工具的意义,恰恰就是让软件贴合你的调试习惯,而不是让你迁就软件的功能预设。

7. 三个让我卡了一阵的坑

坑一:JSON.parse 错误不带位置。 原生只给 Unexpected token x in JSON at position N,但 N 是字符偏移不是行列。我自己写了个 offsetToLineCol(text, offset) 把偏移转成行列——任何面向人的错误提示都要给行列,不给偏移。

坑二:超大 JSON 渲染卡死。 几 MB 的 JSON 全塞进 DOM,浏览器直接冻。改成只渲染前 1000 行 + 虚拟滚动后丝滑。教训:任何"可能很大"的文本渲染,虚拟滚动是必选项,不是优化项。

坑三:循环引用。 JSON.stringify 遇到循环引用会抛错。虽然纯 JSON 不会有循环,但用户偶尔会粘 console.log 打印的对象(带循环)。我用 WeakSet 检测循环并标注 [Circular],不让工具崩。

8. 结尾:开发者工具的"双栏对照"哲学

做完这个 JSON 工具,我对"开发者工具"有了一种更深的体会:

  1. 它不抢你注意力。没有广告、没有登录、没有"猜你想格式化什么";
  2. 它尊重你的数据。JSON 全程本地,带 token 的接口返回放心贴;
  3. 它尊重你的时间。贴进来即格式化,对比即高亮,不需要点"运行"。

在这个所有软件都在抢用户时长的时代,一个"贴即格式化、关掉即走"的本地工具,反而成了一种奢侈。

如果你也常处理 JSON、也想要一个安静的格式化 + 对比工具,我强烈建议自己写一个。技术门槛比想象中低,一个 HTML 文件就够;真正难的是克制住加功能的冲动——不做 JSON Schema 校验、不做"AI 解释字段"、不做"分享我的 JSON",就做一个老老实实的本地处理器。

工具小而静,价值往往不在多,而在"它永远在那里,贴一下就懂"。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。