You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular/Groovy应用9000条Step数据序列化及渲染性能优化咨询

优化Angular/Groovy应用性能(9000条Step数据处理)

哇,150秒的耗时确实让人头疼,咱们从后端Groovy序列化和前端Angular渲染两个核心方向入手,结合你的代码给出具体优化方案:

一、后端Groovy & Hibernate优化

1. 解决Hibernate N+1查询问题(最可能的性能瓶颈)

你当前用Step.findAllByUserName(userName)查询,默认是懒加载关联对象(比如step.week、step.names),这意味着在marshalMinimalStep循环里,每处理一条Step都会触发额外的数据库查询——9000条数据就会产生上万次数据库请求,这绝对是性能杀手!

改成Fetch Join一次性加载所有关联数据,用HQL或者Criteria查询:

def getAll(String userName) {
    // 用HQL一次性查询Step及其关联的week、names,避免N+1
    def steps = Step.executeQuery("SELECT s FROM Step s JOIN FETCH s.week JOIN FETCH s.names WHERE s.userName = :userName", [userName: userName])
    def payload = marshalSteps(steps)
    payload ?: NO_STEPS
}

同时设置Hibernate的fetchSize,减少数据库往返次数:

// 如果用Criteria查询,可以设置fetchSize
def criteria = Step.createCriteria()
def steps = criteria.list(fetchSize: 500) {
    eq("userName", userName)
    fetch "week", JoinType.FETCH
    fetch "names", JoinType.FETCH
}

2. 优化序列化逻辑

你现在手动循环构建Map的方式在9000条数据下开销很大,试试更高效的序列化方案:

  • 用Jackson直接序列化:配置ObjectMapper只序列化需要的字段,比手动循环快得多:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature

private static final ObjectMapper mapper = new ObjectMapper()
    .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
    .addMixIn(Step.class, StepMinimalMixIn.class)

// 用MixIn指定要序列化的字段,避免序列化多余属性
abstract class StepMinimalMixIn {
    abstract Long getId()
    abstract String getName()
    abstract List<Translation> getNames()
    abstract String getShortcut()
    abstract Double getWeek()
    abstract Boolean getIsRelevant()
}

private static def marshalSteps(List<Step> steps) {
    mapper.convertValue(steps, List.class) // 直接转成List<Map>返回
}
  • 避免循环内重复操作:如果marshalTranslation是处理每个step.names的方法,试试批量处理所有names,减少重复调用开销。

3. 强制分页(最立竿见影的优化)

9000条数据一次性返回完全没必要!后端实现分页,比如每页返回100条,前端通过分页控件或滚动加载获取数据:

def getAll(String userName, int page, int pageSize) {
    def steps = Step.createCriteria().list(max: pageSize, offset: (page-1)*pageSize) {
        eq("userName", userName)
        fetch "week", JoinType.FETCH
        fetch "names", JoinType.FETCH
        order("name", "asc") // 后端提前排序,省得前端做
    }
    [data: marshalSteps(steps), total: steps.totalCount]
}

二、前端Angular渲染优化

1. 用虚拟滚动替代全量渲染

ng-repeat渲染9000条DOM元素会直接卡死浏览器,因为每个元素都会创建watchers,DOM节点数量也爆炸。改用虚拟滚动,只渲染当前视口内的元素:

  • 可以用成熟的库比如angular-ui-scroll,或者自定义简单的虚拟滚动逻辑,把DOM节点数量控制在几十条以内。

2. 减少Watchers数量

  • 对不会变化的数据用单向绑定:把{{step.week}}改成{{::step.week}},translations="step.names"如果是静态数据也可以改成单向绑定translations="::step.names"。
  • 移除前端排序:把orderBy:'name'移到后端处理(刚才后端代码里已经加了order("name", "asc")),前端不用再做大量排序计算。

3. 优化DOM结构

你的ng-repeat嵌套了div.col-md-10,可以简化结构,减少不必要的DOM层级:

<ul class="list-group" ng-show="stepCtrl.steps.length > 0">
    <li class="list-group-item overview col-md-10" 
        ng-repeat="step in ::stepCtrl.steps track by step.id"
        ng-click="stepCtrl.edit(step.id)">
        <display-name translations="::step.names"></display-name>
        <span class="pull-right">{{::step.week}}</span>
    </li>
</ul>

总结优化优先级

  1. 先做后端分页,把单次处理的数据量从9000降到100以内,耗时会直接砍到原来的几十分之一;
  2. 解决Hibernate的N+1查询问题,消除上万次数据库请求;
  3. 优化前端渲染,用虚拟滚动减少DOM数量;
  4. 最后再优化序列化细节。

内容的提问来源于stack exchange,提问作者blown.eye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:52:10