【问题标题】:Angular 2 drag and drop directive extremely slowAngular 2拖放指令非常慢
【发布时间】:2016-06-15 20:33:58
【问题描述】:

我正在尝试实现自定义拖放指令。它可以工作,但是速度非常慢,我认为可以跟踪到 Angular 2,因为我以前从未遇到过这种缓慢。仅当我将事件侦听器附加到 dragoverdrag 事件(即频繁发送的事件)时才会发生缓慢,即使我什么都不做,只是在其中返回 false

这是我的指令代码:

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 事件时,一切都会慢到无法忍受的程度!!

编辑我正在使用 angular beta 2.0.0-beta.8

EDIT #2 我尝试使用 chrome 的分析器分析代码,结果如下:

看看标出的线,莫名的可疑……

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

【问题讨论】:

  • 能否简单解释一下问题的原因是什么?读完这个问题我不太明白...
  • 很可能是因为 ng2 和 jQuery 的混合,你试过只依赖 ng2 + RxJS 吗? plnkr.co/edit/LD5FJaI4OOFbKfvhjD4e?p=preview
  • 这不是原因,抱歉,我尝试删除 jQuery 的所有痕迹。结果相同。
  • 那么你必须提供一个复制品。我在我的 plnkr 中看不到相同的行为。
  • @EricMartinez 这是一个 plnkr plnkr.co/edit/cY1Adg5M8Ox0Ss4a6jSn?p=preview 奇怪的是它不会在那里发生!!!你愿意看看我原来的网站,看看有什么不同吗?是吗?

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


【解决方案1】:

刚刚遇到了同样的问题。即使使用高效的ngFor 代码,如果您有大量可拖动项目,拖放操作仍然会非常缓慢。

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

注入:

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

正在初始化:

ngOnInit() {
  this.ngZone.runOutsideAngular(() => {
    el.addEventListener('dragenter', (e) => {
      // do stuff with e or el
    });
...

掉落时:

el.addEventListener('drop', (e) => {
    this.ngZone.run(() => {
        console.log("dropped");
    })
})

【讨论】:

  • 终于找到解决办法了! Drag'nDrop 在涉及许多绑定的树结构上非常缓慢。 10-15 秒的旋转“dragover”事件也不例外。谢谢。
【解决方案2】:

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

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
}

【讨论】:

  • 这个解决方案对我很有效。我只需要将 ChangeDetectorRef 的重新附加移动到单独的“dragend”事件处理程序。否则在取消正在进行的拖动后不会检测到更多更改。
【解决方案3】:

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

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

*ngFor="#a of someFunc()"

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

【讨论】:

  • 你能告诉我 someFunc() 有什么吗?
  • 你能告诉我 someFunc() 有什么吗?
  • 它在每次调用中动态创建元素列表,一个新副本。
【解决方案4】:

我最近遇到了类似的问题。它在使用反应形式的 Angular 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();
        // ...

如果您有很多父组件或分层组件,您可能还需要对它们的更改检测做一些事情,才能看到实质性的改进。

【讨论】:

    【解决方案5】:

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

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

    如果有人能提供一个替代的 dragOver 事件处理程序来绕过更改检测,那就太好了。

    【讨论】:

      【解决方案6】:

      我遇到了类似的问题,当我在 *ngFor 中放置多个拖动区域时,我的拖放变得非常缓慢。

      我通过将更改检测策略更改为子组件的OnPush 解决了这个问题。

      然后在每次拖动项目时,执行markForCheck()

      constructor(private changeDetectorRef: ChangeDetectorRef) {}
        
      // Callback function
      public onDrag() {
        this.changeDetectorRef.markForCheck();
      }

      【讨论】:

        【解决方案7】:

        对我来说,问题是即使在生产中也打开了开发模式。当我用ng build --evn-prod 编译它时,拖放突然变得非常快。

        【讨论】:

          【解决方案8】:

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

          constructor() {
              $(document).delegate('#jsDragZone', 'dragenter', function (e) {
          
                 console.log('here your logic')
          
              });
          }
          

          通过这种方式,您也可以实现其他事件(dragleave、drop、'dragover')。它对我来说非常好用而且速度很快。

          【讨论】:

            猜你喜欢
            • 2019-04-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-11
            • 1970-01-01
            • 1970-01-01
            • 2019-12-13
            • 2018-05-08
            相关资源
            最近更新 更多