【问题标题】:Angular6 pipe filter run slow on large arrayAngular6管道过滤器在大型阵列上运行缓慢
【发布时间】:2018-08-16 22:02:11
【问题描述】:

我有这个过滤器,它从一个大数组(这个数组中大约 500 个项目)中返回一个项目的子集

    import { Injectable, Pipe, PipeTransform } from '@angular/core';

    @Pipe({
     name: 'searchFilter'
    })

    @Injectable()
    export class FilterArrayPipe implements PipeTransform {
     transform(value: any, ...args): any {
        // tslint:disable-next-line:no-unused-expression
        console.log(value, args);
       if ( typeof args[0] === 'undefined') {
        return value;
       } else if (value) {
           return value.filter(item => {
               // tslint:disable-next-line:prefer-const
               for (let key in item) {
                   if ((typeof item[key] === 'string' || item[key] instanceof String) &&
                        (item[key].toUpperCase().indexOf(args[0].toUpperCase()) !== -1)) {
                            return true;
                        }
               }
           });
       }
    }

    }

每当我在文本框中键入时,字符实际显示在框中需要几秒钟,然后再返回几秒钟更新列表以返回搜索项。

我尝试添加slice:0:10 以限制屏幕上的项目数量,但它有同样的问题,所以我猜测速度缓慢是由于大数组而不是屏幕渲染。

我已经和后端开发人员谈过了,由于这个数组被其他人使用,他不会修改它。我能做些什么来提高性能?

编辑:包含 html 代码:

    <form *ngFor='let subarray of array | searchFilter: filterText | slice:0:20 ; let i = index;' #form="ngForm" (ngSubmit)="Save(form)">
      <fieldset>
            <input type="text" name="country" [ngModel]="subarray.country ">
            <input type="number" name="number" [ngModel]="subarray.number ">
            ............about 24 input fileds in total ..............
       </fieldset>
    </form>

【问题讨论】:

  • 能否请您添加组件的 HTML 模板的相关部分。
  • 你好,首先删除控制台日志,如果你有一个大的打印会很慢,第二个值是500大小的数组?如果是这样,每个项目的大小是多少
  • @cyrix 包含了 html 代码
  • 为数组中的 500 个条目添加 24 个 dom 元素将是昂贵的,期间。我能想到的大幅提高性能的最好方法是使用某种虚拟滚动,但这样做时你必须记住的是每个元素的高度必须完全相同。如果不能保证,就会遇到滚动问题。
  • @AmitWagner 嗯。 . .我删除了控制台日志,它确实有点帮助。但是文本框和结果仍然有一点延迟。每个项目有大约 6 个属性和一个数组,但该数组最多有 13 个属性

标签: javascript angular angular6


【解决方案1】:

您应该尝试一些性能改进:

对 ngForOf 使用 trackBy-function

使用为ngForOf 指令提供trackBy 函数,例如通过索引跟踪所有DOM 元素:

// template:
<div *ngFor="let subarray of array; trackBy:trackByIndex">
  ...
</div>

// component:

public trackByIndex(index: number, value: any)
{
  return index;
}

不要使用管道过滤数据 (对于不纯的管道)

不要使用管道来过滤大型数组,因为每次为组件运行 changedetection 时都会调用它。

例如,如果搜索输入发生变化,您可以过滤大型数组:

// component:
public originalData: any[] = [...];
public filteredData: any[] = this.originalData;

// ...

public filterData(searchString: string)
{
   this.filteredData = this.originalData.filter(item => {
     // your filter logic
   })
}

通过可搜索的字符串丰富您的每个项目

通过可搜索的字符串丰富数组中的每个项目。如果您想搜索一个项目的所有值,只需将数组映射一次并将一个新键附加到该项目,如:

this.originalData = this.dataService.getData().map(item => {
   return {
     ...item,
     searchableString: Object.values(item).join(':'),
   }
})

这可以防止您在过滤时遍历每个项目。您只需在该属性内搜索输入字符串。

item.searchableString.indexOf(input) > -1

其他技术

  • 去抖动用户搜索输入
  • 使用 BehaviorSubject 和 ChangeDetectionStrategy.OnPush 过滤服务内的数据,并将数据作为列表组件的输入

旁注

请记住,Angular 在开发模式下的性能远比在生产模式下差。在开发模式下,每个更改检测都会运行两次。 AOT-mode也会带来很多性能提升。

【讨论】:

  • 去抖搜索词输入也没有什么坏处。
  • 正要添加 ;) 还有很多选项,例如将服务与主题和 onPush 更改检测一起使用
  • Don't use a pipe to filter your large array, because it'll be invoked everytime the changedetection runs for the component.今天又查了一下,是假的,很多人告诉别人要避免,虽然它对性能没有影响,只要输入相同,管道变换方法就不会叫做。您应该不惜一切代价避免数据绑定函数调用。证明:stackblitz.com/edit/…
  • 要么我遗漏了什么,要么他没有将管道设置为pure: false。无论如何,我认为在你的答案中添加一个关于它的注释会很棒。
  • @Ploppy 告诉其他人避免它的“许多人”实际上是 Angular 团队:angular.io/guide/pipes#no-filter-pipe 您可以通过在阵列上强制执行不变性来避免性能下降,但可维护性、可重用性和易用性当您不得不重新引用它时,了解远远低于自己过滤数组。
【解决方案2】:

您需要记住过滤器的先前状态才能获得性能。

假设您有一个包含 500 个字符串的长列表,看起来像这样。

const originalValues = ['a', 'and', 'goo', 'apple', 'antelope', ...];

当用户输入a 时,您可以像这样过滤上述值。

const newValues = values.filter((value) => value.indexOf(input) !== -1);
console.log(newValues);

上面会打印这样的东西。

['a', 'and', 'apple', 'antelope']

用户键入的下一个字母会更新搜索 input,您可以再次过滤 newValues。每次用户在他们的输入词中添加一个字母时,您都可以再次过滤 previous 结果。只要用户使输入字符串更长。

这不能通过管道完成,因为管道不记得以前的状态。您必须在组件中做一些特殊的事情来处理这个问题。

当您达到结果限制时,您还需要中止。这样,当您发现限制为 20 时,您就不会迭代整个集合。

 const newValues = [];
 for(let value of values) {
     if(newValues.length === 20) {
          break;
     }
     if(value.indexOf(input) !== -1) {
        newValues.push(value);
     }
 }

【讨论】:

    【解决方案3】:

    另一种可能性是记忆模式。

    如果,例如,那是你的管道

    // excerpt
    transform(value: any, ...args: any[]): any {
       return this.myOwnTransformingMethod(args, value);
    }
    

    你可以在这个方法中使用备忘录装饰器,比如

    @memo()
    private myOwnTransformingMethod(args: any[], value: any) {
    // do some filtering or transformations and
    return value;
    }
    

    您不必自己实现该模式。您可以使用一个(MIT 许可证):

    npm i memo-decorator -S
    

    并将其导入您的whatever.pipe.ts

    import memo from 'memo-decorator';
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多