优化大型JSON响应

12

我正在开发一个大数据客户端应用程序,服务器语言是Java。在前端中,我使用原始的JavaScript作为主要语言,但使用AngularJS作为MVC框架。

问题

处理大量数据分析时,单个REST API响应的大小通常在1.5MB至3MB之间。将这些数据处理以构建DOM非常困难。

  • 首先需要花费约5到10秒钟来加载JSON。
  • 然后构建UI(DOM)
  • 一旦构建完DOM,基于用户与数据的交互 - 我必须将带有更新值的相同JSON发送/返回到服务器。

请建议我优化页面响应速度的选项。

  • 我想到了一些事情:
  • 将JSON分成每次1000个块,一旦DOM加载完成,再静默地带入数据并更新UI。
  • 在服务器上GZIP JSON并在客户端解码回来。

给我你的具体解决方案!

示例JSON可能如下:

var data = [
    {
        prop:val,
        prop2: {},
        prop3:[
            id: val,
            prop4: { {}, {}, {}, {}},
            prop5: [ [], [], [] ]
        ]
    },
    {},
    {},
    {}
]

一些使用案例

  • 数据大小可能为10000个对象,嵌套至少六七层。
  • 需要构建网格(表),行的长度与对象相同,列至少有100列。
  • 所有数据单元格都有自定义上下文菜单,具有嵌套标题,所有列都可排序,行也可以排序,并且这些排序顺序在用户更改后立即传达到服务器。但我有第二时间阈值。

一个非常基本的例子在这里:http://shekhardesigner.github.io/OuchGrid/


1
你好,我不知道你的设计如何,但是想过懒加载吗?也许你正在展示一个很大的数据列表,可以只加载用户可见的部分。这里有一个链接到ngInfiniteScroll(http://binarymuse.github.io/ngInfiniteScroll/) - 它正是我所说的那样。 - Jonas
请问您能否提供一份您发送给客户端的数据样本? - Yury Tarabanko
我只需要一个结构,而不是数据本身。假装一下哈哈 - Yury Tarabanko
尝试使用gzip,但没有帮助。连接足够良好。这是一个内部应用程序,因此用户处于高管级别。唯一的问题是需要构建一次超过10,000行X列的网格。 - absqueued
如果你有一个对象数组 [{name: value},{name: value}...10000],你可以将其替换为一个二维数组 [[value], [value] ...],通过消除重复的属性名,从而大幅减少有效载荷大小。 - Yury Tarabanko
显示剩余2条评论
4个回答

9

以下是我的建议:

  1. 首先从服务器端对响应数据进行分页,以减小json对象的大小。
  2. 通过分块并行渲染UI。
  3. 如果数据太大,请勿在数据中进行深层监视,例如不要为过多的数据进行ngRepeat,如果双向绑定不必要,则会使您的应用程序非常缓慢。

我已经尽可能地进行了Angular优化,例如避免嵌套ngRepeat、使用bindonce、使用trackby、构建自定义指令来处理谨慎的拦截器。 - absqueued
一个单独的REST API响应大约1.5MB到3MB是没有意义的,你是否已经从服务器端分页你的数据?或者你是从客户端进行分页的? - Sean
服务器端尚未实现分页功能 - 这是我接下来要考虑的选项。客户端分页不是必需的,因此我正在考虑使用懒加载,但也在考虑响应性。 - absqueued
是的,在服务器端进行分页非常重要。如果数据是静态的,也就是说它是不可变的,那么考虑使用模板直接渲染HTML。 - Sean

5

"首先需要5到10秒钟来加载JSON。 然后我构建UI(DOM)"

  1. 这两个步骤不能异步进行吗?例如,加载DOM并等待ajax回调?

  2. 我不确定是否有办法,因为缺乏细节,但也许您想重新考虑整个加载过程,只在需要时加载“较小的对象”。

  3. 考虑对对象/字符串进行压缩

这是我现在能想到的前三种优化方式,具体取决于您的用例,您可能能够采纳这些建议

希望这可以帮助您-请随意提供反馈


保持两个动作同步有两个问题:1.)用户一看到 UI 就开始与之交互,我必须提交更新。2.)一旦收到数据就立即更新 DOM 是一个瓶颈,因为响应在这里非常巨大。我说的是每次同时涉及 10000 多行,最少有 100 列。如果我不断更新 DOM,则浏览器会冻结。FYA,这是一个分析应用程序。 - absqueued
1
浏览器冻结意味着您正在使用所有这些更新来垃圾堆栈...您没有给渲染队列足够的时间重新渲染页面->冻结。您可以通过将DOM更改逐个放入队列中来异步执行DOM更新..这允许渲染队列在其间进行“渲染”,并且看起来更加流畅...不确定是否也适用于您。 - Max Bumaye
如果您想批量更新DOM,请在其周围放置setTimeout(update, 0); /update是您的DOM更新函数/,并将其推入队列中...用户将看到DOM更改应用,因为他仍然能够交互,因为渲染队列可以在您的update()调用之间推送其渲染。 - Max Bumaye
100列?您要同时显示全部100列吗? - Petr Averyanov
当你从服务器获取所有数据时,在客户端只需考虑将“小部分”推送到队列中,它将比等待更新EVERYTHING更好渲染。 ;D - Max Bumaye
@MaxBumaye 看起来值得一试的想法。Petr-averyanov 是的,必须有列。其中100个中,可以固定10个以上进行比较,其他的可以水平滚动。 - absqueued

3
对于我的一个作业,我们使用了自己的解决方案。大部分数据都是集合或数组,因此我们实现了简单的算法来删除所有冗余的属性名称,并仅保留一组属性名称和值对象的集合。我们看到了相当不错的尺寸减小,可能在下一级别中还可以使用一些库进一步解压缩。但需要注意的是,这仅适用于处理结构化数据,如果数组对象具有不同的结构,则可能需要不同的算法。
其他算法,比如我推荐的,可以考虑使用自然压缩技术,例如从JSON中删除所有空格,使字段名称变小。
另外,您可以查看像Protocol Buffers这样的规范,这可能会产生更小的上传大小。请参考https://github.com/dcodeIO/ProtoBuf.js 如果处理数据需要时间并且会冻结屏幕渲染,您可以尝试使用Web Workers(适用于最新的浏览器)来卸载处理逻辑,主线程/事件循环仍然可用于UI渲染或响应用户操作。

我已经开始使用Web Worker了。协议缓冲和其他压缩技术将是架构级别的更改,因此稍后再考虑。 - absqueued
你好,你如何使用Web Workers实现? - Sivaprabu Ganesan

1
"首先需要大约5到10秒钟来加载JSON。然后我构建UI(DOM)"
"我建议您异步加载UI和数据,但禁止用户在适当的数据加载之前执行某些操作。"
"一旦数据加载到您的变量/服务中,请使用前端分页来最小化浏览器的压力。JavaScript可以存储大量数据,但DOM将难以渲染大量的HTML。"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接