baidu / amis

前端低代码框架,通过 JSON 配置就能生成各种页面。
https://baidu.github.io/amis/
Apache License 2.0
17.19k stars 2.5k forks source link

input-table组件渲染80条数据的时候,遇到特别卡顿的情况需要10秒钟才能加载完成 #10724

Open Bamzc opened 2 months ago

Bamzc commented 2 months ago

描述问题:

我最近在使用 input-tabel 渲染 80 条数据的时候,遇到特别卡顿的问题需要 10 秒钟才能加载完成

截图或视频:

image

使用方式&版本:

  1. 你是如何使用 amis 的? SDK

  2. amis 版本是什么? 6.6.0

问题排查

我排查了源码,再根据我的实际用法有几个疑问

  1. 我的实际使用方式: 我有两列使用 input-number inline 模式的可编辑数字框,这个数字的小数位数是 5(实际后端返回的数据也是 5 位,比如 25.10000)

  2. 源码中 QuickEdit: 在 render 这个组件的时候传入一个 onChange 方法,实际是 handleFormItemChange,我发现这个 handleFormItemChange 传入的 value 值,实际是 25.1,导致 onQuickChange 这个方法执行了 160 次,不停的更新表格变得异常卡顿

问题尝试改进

我在这个 handleFormItemChange 方法里面加入了,这个判断。瞬间渲染时间从 10 秒,减少 5 秒以内(4 秒多一点点)。。。

问下这个能这么改吗?是哪里做的这个控制呢?

if (
  quickEdit?.type === 'input-number' &&
  parseFloat(oldValue) === value
) {
  return;
}
Bamzc commented 2 months ago
1230787ce0c20f7db6188b218f5867b6

最终原因:inputNumber自动把 string 转 number 解决方案:配置成 big: true(代码逻辑如果配置成 big true,不会做数字转换)

238040a45e56e80188b6747d16c65706

最后渲染时间瞬间减少一半:4.7秒

但是还是很慢很慢的...

EvanZheng11 commented 2 months ago

mark

2betop commented 2 months ago

可以先加个 perPage:10 启动分页

2betop commented 2 months ago

@Bamzc 时间是怎么打的?

2betop commented 2 months ago

问题 schema

export default {
  type: 'page',
  body: {
    type: 'form',
    name: 'form1',
    data: {},
    api: '/api/mock2/form/saveForm',
    onEvent: {
      inited: {
        actions: [
          {
            actionType: 'custom',
            script: function () {
              const form = this.form1;

              setTimeout(() => {
                const startTime = performance.now();
                console.error('数据开始渲染');
                form.setValues({
                  table: new Array(80).fill().map((item, index) => ({
                    a1: 'a1',
                    b2: 'b1',
                    a3: `3${index}.2344`,
                    a4: '',
                    a5: index % 3 === 0
                  }))
                });
                requestAnimationFrame(() => {
                  const endTime = performance.now();
                  console.error(
                    `表格渲染到用户彻底看到页面所需要时间: ${
                      (endTime - startTime) / 1000
                    } s`
                  );
                });
              }, 3000);
            }
          }
        ]
      }
    },
    body: [
      {
        type: 'input-table',
        name: 'table',
        label: 'Table',
        addable: true,
        needConfirm: false,
        // perPage: 10,
        columns: [
          {
            label: 'A1',
            name: 'a1',
            type: 'input-text'
          },
          {
            label: 'A2',
            name: 'a2',
            type: 'select',
            options: ['b1', 'b2', 'b3']
          },
          {
            label: 'A3',
            name: 'a3',
            type: 'input-number'
          },
          {
            label: 'A4',
            name: 'a4',
            type: 'input-text'
          },
          {
            label: 'A5',
            name: 'a5',
            type: 'switch'
          },
          {
            label: 'A6',
            name: 'a6',
            type: 'input-text'
          },
          {
            label: 'A7',
            name: 'a7',
            type: 'input-text'
          },
          {
            label: 'A8',
            name: 'a8',
            type: 'input-text'
          },
          {
            label: 'A9',
            name: 'a9',
            type: 'input-text'
          },
          {
            label: 'A10',
            name: 'a10',
            type: 'input-text'
          },
          {
            label: 'A11',
            name: 'a11',
            type: 'input-text'
          },
          {
            label: 'A12',
            name: 'a12',
            type: 'input-text'
          },
          {
            label: 'A13',
            name: 'a13',
            type: 'input-text'
          },
          {
            label: 'A14',
            name: 'a14',
            type: 'input-text'
          },
          {
            label: 'A15',
            name: 'a15',
            type: 'input-text'
          },
          {
            label: 'A16',
            name: 'a16',
            type: 'input-text'
          },
          {
            label: 'A17',
            name: 'a17',
            type: 'input-text'
          }
        ]
      }
    ]
  }
};
Bamzc commented 2 months ago

@Bamzc 时间是怎么打的?

setTimeout(() => {

  const table = amisScoped.getComponentById('table_id');
  const startTime = performance.now();
  console.error('数据开始渲染');
  table.props.store.updateData({
    "table_bind_name": datas
  })
  requestAnimationFrame(() => {
    const endTime = performance.now();
    console.error(`表格渲染到用户彻底看到页面所需要时间: ${(endTime - startTime) / 1000} s`);
  });
}, 3000);

我是这样模拟的 @2betop

Bamzc commented 2 months ago

可以先加个 perPage:10 启动分页

我是这么处理的:

把table的懒加载配置,让input-table也支持这个配置。

lazyRenderAfter: 30 // 实际是 80条数据 ==> 当一次性渲染太多列上使用,可以用来提升表格渲染性能

渲染30条数据的时间是1.4秒(这个用户可以接受~) @2betop