Angular6管道过滤器在大型阵列上运行缓慢

时间:2018-08-16 22:02:12

标签: javascript angular angular6

我有这个过滤器,可以从一个大数组中返回部分项目(此数组中大约有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>

3 个答案:

答案 0 :(得分:3)

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

对ngForOf使用trackBy函数

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

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

// component:

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

请勿使用管道过滤数据 (用于不纯管道)

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

例如,如果搜索输入发生更改,则可以过滤大型数组:

// 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一起过滤服务内部的数据,并将数据作为列表组件的输入

边注

请记住,角度开发模式下的性能远比生产模式下的性能差。在开发模式下,每个变更检测运行两次。此外,AOT模式将带来很多性能改进。

答案 1 :(得分:0)

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

让我们假设您有一长串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。每次用户在输入词中添加字母时,您都可以再次过滤上一个结果。只要用户使输入字符串变长。

这不能通过管道完成,因为管道不记得先前的状态。您必须在组件中执行一些特殊的操作来处理此问题。

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

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

答案 2 :(得分:0)

另一种可能是记忆模式。

例如,如果那是您的管道

// 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';