Angular 2拖放指令极其缓慢

时间:2016-03-02 19:37:47

标签: drag-and-drop angular angular2-directives

我正在尝试实现自定义拖放指令。它有效,但它非常慢,我认为可以跟踪Angular 2的缓慢,因为我以前从未遇到过这种缓慢。只有当我将事件监听器附加到dragoverdrag事件(即频繁发送的事件)时,才会出现缓慢的情况,即使我什么也没做,只返回false。< / p>

这是我的指令代码:

import {Directive, ElementRef, Inject, Injectable} from 'angular2/core';

declare var jQuery: any;
declare var document: any;

@Directive({
    selector: '.my-log',
    host: {
        '(dragstart)': 'onDragStart($event)',
        '(dragover)': 'onDragOver($event)',
        '(dragleave)': 'onDragLeave($event)',
        '(dragenter)': 'onDragEnter($event)',
        '(drop)': 'onDrop($event)',
    }
})
@Injectable()
export class DraggableDirective {
    refcount = 0;
    jel;

    constructor( @Inject(ElementRef) private el: ElementRef) {
        el.nativeElement.setAttribute('draggable', 'true');
        this.jel = jQuery(el.nativeElement);
    }

    onDragStart(ev) {
        ev.dataTransfer.setData('Text', ev.target.id);
    }

    onDragOver(ev) {
        return false;
    }

    onDragEnter(ev) {
        if (this.refcount === 0) {
            this.jel.addClass('my-dragging-over');
        }
        this.refcount++;
    }

    onDragLeave(ev) {
        this.refcount--;
        if (this.refcount === 0) {
            this.jel.removeClass('my-dragging-over');
        }
    }

    onDrop(ev) {
        this.jel.removeClass('my-dragging-over');
        this.refcount = 0;
    }
}

以下是相关的样式表摘录:

.my-log.my-dragging-over {
    background-color: yellow;
}

正如你所看到的,我正在做的是突出显示被黄色拖动的元素。当我不处理dragover事件时它会很快工作,但我必须处理它以支持删除。当我处理dragover事件时,一切都会变慢到无法忍受的水平!!

编辑我正在使用角度测试版2.0.0-beta.8

编辑#2 我尝试使用chrome的分析器对代码进行分析,结果如下:

Imgur

看看标记的线,这是奇怪的可疑......

编辑#3 发现问题:确实是由于Angular 2的变化检测。在我的案例中,拖放操作是在一个非常密集的页面上完成的,它有很多绑定和指令。当我评论除了给定列表之外的所有内容时,它再次快速运行...现在我需要你的帮助来找到解决方案!

编辑#4已解决

问题确实是变化检测,但错误不在于Angular的代码,而在于我自己的低效绑定。我有很多这种绑定:

*ngFor="#a of someFunc()"

这导致Angular不确定数据是否已更改,并且即使数据在拖放过程中没有发生变化,也会反复调用函数someFunc。我更改了这些绑定以引用我的类中的简单属性,并将填充它们的代码移动到它应该的位置。一切都开始快速闪电了!

谢谢!

8 个答案:

答案 0 :(得分:7)

刚遇到同样问题的麻烦。即使使用高效的launchMode代码,如果你有大量的可拖动项目,拖放仍然会很慢。

对我来说,诀窍是让所有拖放事件监听器在Angular之外运行alwaysRetainTaskState,然后在删除时使其在Angular中运行。这使Angular避免检查您移动可拖动项目的每个像素的检测。

注入:

ngFor

初​​始化:

ngZone

暂停:

import { Directive, ElementRef, NgZone } from '@angular/core';
constructor(private el: ElementRef, private ngZone: NgZone) {}

答案 1 :(得分:4)

回答我自己的问题(问题已经解决)。

缓慢问题是由于我的标记中的数据绑定效率低下导致Angular浪费了大量时间在我的视图模型上调用函数。我有很多这种绑定:

*ngFor="#a of someFunc()"

这导致Angular不确定数据是否发生了变化,并且每次运行someFunc后都会反复调用函数onDragOver(即大约每350毫秒一次),即使在拖放过程中数据没有变化。我更改了这些绑定以引用我的类中的简单属性,并将填充它们的代码移动到它应该的位置。一切都开始快速闪电了!

LLAP!

答案 2 :(得分:4)

感谢大家的讨论。 最后得到一个简单的解决方案,就像一个魅力:

constructor(private cd: ChangeDetectorRef) {
}

drag(event: DragEvent): void {
    this.cd.detach();
    // Begin the job (use event.dataTransfer)
}

allowDrop(event: DragEvent): void {
    event.preventDefault();
}

drop(event: DragEvent): void {
    event.preventDefault();
    this.cd.reattach();
    // Do the job
}

答案 3 :(得分:1)

我最近有一个类似的问题。它处于使用反应形式的6号角环境中。这是我针对自己的情况解决的方法:

基本上,我在拖动时关闭了该组件上的更改检测。

  1. 导入ChangeDetectorRef:
    import { ChangeDetectorRef } from '@angular/core';
  1. 将其注入到构造函数中:
    constructor(private chngDetRef: ChangeDetectorRef) { //...
  1. 在dragStart上将其分离:
    private onDragStart(event, dragSource, dragIndex) {
        // ...
        this.chngDetRef.detach();
        // ...
  1. 在拖放时将其重新连接:
    private onDrop(event, dragSource, dragIndex) {
        // ...
        this.chngDetRef.reattach();
        // ...

    private onDragEnd(event, dragIndex) {
        // ...
        this.chngDetRef.reattach();
        // ...

如果您有很多父级或分层组件,则可能还必须对它们的更改检测进行一些操作才能看到实质性的改进。

祝你好运!

答案 4 :(得分:1)

这是旧文章的后续内容,但是拖放仍然是“一个问题。我的特殊问题涉及页面上包含130多个组件并且拖放非常糟糕。我尝试了本文中提供的各种建议和其他帖子,但改进很少。

最后,我决定尝试将ngZone更改为原生(dragOver)="function()",而不是ondragover="event.preventDefault()"解决方案。我让所有其他事件处理程序(即dragStartdragEnterdragLeavedragDropdragEnd)根据需要通过Angular。我的拖放响应从几秒变成了几毫秒。

任何人都可以提供一个替代dragOver事件处理程序来绕过更改检测,那就太好了。

答案 5 :(得分:0)

我有一个类似的问题,当我在//Copying data from sheet 2 to sheet 1 var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2"); var destSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1"); sourceSheet.getRange("E:G").copyTo(destSheet.getRange("E:G"), {contentsOnly:true}); 内放置多个拖动区域时,我的拖放变得非常慢。

我通过将更改检测策略更改为子组件的*ngFor来解决此问题。

然后在每次拖动项目时,执行OnPush

markForCheck()

答案 6 :(得分:0)

对我来说问题是即使在生产中也开启了开发模式。当我使用 Select RegisterNumber, count(distinct StartDate || '-' || Starttime) AS TOTALDOWNLOAD FROM `SAMPLE.csv` WHERE (MaintenanceFlightTime > 0) GROUP BY RegisterNumber 进行编译时,拖放突然变得非常快。

答案 7 :(得分:0)

我在角度项目中存在相同的拖放问题-detectChanges(reattach(),deTached ..),outSide Angular(ngZone)无法解决此问题。 现在,我通过使用jquery解决了这个问题,我在div内容的构造函数中绑定了事件。

constructor() {
    $(document).delegate('#jsDragZone', 'dragenter', function (e) {

       console.log('here your logic')

    });
}

这样,您还可以实现其他事件(拖动,拖放,“拖动”)。对我来说,它工作得非常好而且很快。