Реактивность и обновление

SortableJS — это мощная библиотека для создания интерактивных списков с поддержкой перетаскивания элементов. Одной из ключевых особенностей при интеграции с современными фреймворками является обеспечение реактивности и корректного обновления состояния списка после изменений.


Связывание с реактивными данными

В реактивных фреймворках, таких как Vue или React, состояние элементов часто хранится в массивах. SortableJS по умолчанию управляет DOM-элементами, но не реактивным массивом напрямую. Для корректного взаимодействия необходимо синхронизировать события библиотеки с массивом данных.

Пример базового связывания с массивом:

import Sortable from 'sortablejs';

const items = ['Элемент 1', 'Элемент 2', 'Элемент 3'];

const listElement = document.getElementById('sortable-list');

Sortable.create(listElement, {
  animation: 150,
  onEnd: (evt) => {
    const [movedItem] = items.splice(evt.oldIndex, 1);
    items.splice(evt.newIndex, 0, movedItem);
  }
});

Ключевые моменты:

  • onEnd вызывается после завершения перетаскивания.
  • Массив items обновляется в точном соответствии с изменениями DOM.
  • splice используется для удаления и вставки элемента, обеспечивая сохранение порядка элементов.

Обновление списка при динамических изменениях

Если элементы списка изменяются динамически (например, добавляются или удаляются новые элементы), необходимо обновлять экземпляр SortableJS с помощью метода destroy() и последующего повторного создания, либо использовать Sortable.update().

// Обновление после добавления нового элемента
items.push('Новый элемент');
listElement.innerHTML = items.map(item => `<li>${item}</li>`).join('');

sortableInstance.destroy();
sortableInstance = Sortable.create(listElement, {
  animation: 150,
  onEnd: (evt) => {
    const [movedItem] = items.splice(evt.oldIndex, 1);
    items.splice(evt.newIndex, 0, movedItem);
  }
});

Особенности:

  • Пересоздание Sortable обеспечивает актуальность всех событий и индексов.
  • Прямое обновление DOM необходимо, если библиотека не управляет элементами через виртуальный DOM.

Реактивность в Vue

В Vue 3 с Composition API управление списком можно сделать более «нативным»:

import { ref, onMounted } from 'vue';
import Sortable from 'sortablejs';

export default {
  setup() {
    const items = ref(['Элемент A', 'Элемент B', 'Элемент C']);
    let sortableInstance;

    onMounted(() => {
      sortableInstance = Sortable.create(document.getElementById('sortable-list'), {
        animation: 150,
        onEnd: (evt) => {
          const [moved] = items.value.splice(evt.oldIndex, 1);
          items.value.splice(evt.newIndex, 0, moved);
        }
      });
    });

    return { items };
  }
}

Примечания по реактивности:

  • Использование ref позволяет Vue автоматически отслеживать изменения массива.
  • Массив items.value синхронизируется с DOM, поддерживая реактивность интерфейса.
  • Событие onEnd гарантирует, что порядок элементов в массиве и на экране совпадает.

Оптимизация при больших списках

Для списков с сотнями элементов рекомендуется:

  1. Использовать animation: 0 при частых обновлениях, чтобы избежать задержек рендеринга.
  2. Минимизировать пересоздание экземпляра SortableJS, ограничивая операции destroy и create только при необходимости.
  3. Применять виртуализацию DOM (например, с virtual-scroll), чтобы Sortable работал только с видимыми элементами.

Работа с группами и взаимной реактивностью

SortableJS поддерживает группы элементов, что позволяет перетаскивать объекты между списками. Для реактивного управления требуется двусторонняя синхронизация массивов.

const listA = ['A1', 'A2'];
const listB = ['B1', 'B2'];

Sortable.create(document.getElementById('list-a'), {
  group: 'shared',
  onAdd: (evt) => {
    const [item] = listB.splice(evt.oldIndex, 1);
    listA.splice(evt.newIndex, 0, item);
  }
});

Sortable.create(document.getElementById('list-b'), {
  group: 'shared',
  onAdd: (evt) => {
    const [item] = listA.splice(evt.oldIndex, 1);
    listB.splice(evt.newIndex, 0, item);
  }
});

Особенности синхронизации:

  • onAdd вызывается при добавлении элемента из другой группы.
  • Массивы обоих списков обновляются в точном соответствии с действиями пользователя.
  • Обеспечивается консистентность данных и DOM.

Дополнительные рекомендации

  • onUpdate используется, если требуется обработка изменений без удаления элемента.
  • onMove позволяет предотвратить перемещение по условию.
  • Для больших и динамически изменяемых интерфейсов рекомендуется поддерживать чистый и реактивный массив, чтобы SortableJS управлял только визуальной составляющей.

Эффективное использование SortableJS в реактивных приложениях требует внимательного контроля синхронизации между массивами данных и DOM. Корректная реализация событий onEnd, onAdd, onUpdate позволяет создать интерактивный, устойчивый и масштабируемый интерфейс перетаскивания элементов.