Оптимистичные обновления

SortableJS — это лёгкая и мощная библиотека для реализации перетаскивания элементов на веб-страницах с помощью Drag-and-Drop API. Она поддерживает множество возможностей: сортировку списков, перенос элементов между списками, работу с вложенными структурами, а также оптимистичные обновления данных.

Инициализация

Для начала работы необходимо подключить библиотеку к проекту через npm или CDN:

import Sortable from 'sortablejs';

const list = document.getElementById('items');
Sortable.create(list, {
    animation: 150,
    ghostClass: 'sortable-ghost'
});
  • animation — определяет скорость анимации перемещения элементов.
  • ghostClass — CSS-класс, применяемый к элементу во время перетаскивания.

Основные опции

  • group — позволяет объединять несколько списков, чтобы элементы можно было перетаскивать между ними.
  • draggable — селектор элементов, доступных для перетаскивания.
  • handle — селектор элементов, за которые можно захватывать объект.
  • onEnd, onAdd, onUpdate — колбэки для обработки событий после завершения перетаскивания, добавления или изменения порядка элементов.

Пример конфигурации с обработкой событий:

Sortable.create(list, {
    animation: 200,
    handle: '.handle',
    onEnd: function (evt) {
        console.log(`Элемент перемещён с позиции ${evt.oldIndex} на ${evt.newIndex}`);
    },
    onAdd: function (evt) {
        console.log('Элемент добавлен в список');
    }
});

Оптимистичные обновления

Оптимистичный подход предполагает немедленное обновление интерфейса при перетаскивании, ещё до подтверждения изменений на сервере. Это обеспечивает плавный пользовательский опыт. В контексте SortableJS оптимистичные обновления реализуются через:

  • Немедленное изменение DOM или состояния данных после события onEnd.
  • Асинхронную синхронизацию с сервером с обработкой ошибок для отката изменений при необходимости.

Пример оптимистичного обновления списка задач:

const tasks = [
    { id: 1, title: 'Сделать задачу A' },
    { id: 2, title: 'Сделать задачу B' },
    { id: 3, title: 'Сделать задачу C' }
];

const listElement = document.getElementById('tasks');

function renderTasks() {
    listElement.innerHTML = '';
    tasks.forEach(task => {
        const li = document.createElement('li');
        li.dataset.id = task.id;
        li.classList.add('task-item');
        li.innerHTML = `<span class="handle">☰</span> ${task.title}`;
        listElement.appendChild(li);
    });
}

renderTasks();

Sortable.create(listElement, {
    animation: 150,
    handle: '.handle',
    onEnd: function (evt) {
        const [movedTask] = tasks.splice(evt.oldIndex, 1);
        tasks.splice(evt.newIndex, 0, movedTask);
        // Отправка обновлённого порядка на сервер
        fetch('/updateTasksOrder', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(tasks.map(t => t.id))
        }).catch(() => {
            // Откат изменений при ошибке
            tasks.splice(evt.newIndex, 1);
            tasks.splice(evt.oldIndex, 0, movedTask);
            renderTasks();
        });
    }
});

Ключевые моменты оптимистичных обновлений:

  1. Сразу обновлять DOM для визуальной реакции пользователя.
  2. Синхронизация с сервером в фоне без блокировки интерфейса.
  3. Обработка ошибок с возможностью отката изменений.
  4. Поддержка анимации для плавного перетаскивания и ощущения интерактивности.

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

SortableJS поддерживает перенос элементов между списками. Опция group позволяет задавать группы и правила перемещения:

Sortable.create(list1, { group: 'shared', animation: 150 });
Sortable.create(list2, { group: 'shared', animation: 150 });
  • Элементы можно перемещать между списками одной группы.
  • Опция pull контролирует возможность извлечения элементов из списка.
  • Опция put определяет возможность вставки элементов в список.

Расширенные возможности

  • Фильтрация элементов с помощью filter позволяет блокировать перетаскивание некоторых элементов.
  • События onStart, onMove, onClone обеспечивают гибкий контроль за поведением при начале перемещения, во время движения и при клонировании элемента.
  • Поддержка вложенных списков через nestedSortable упрощает работу с древовидными структурами данных.

Рекомендации по производительности

  1. Ограничивать количество DOM-операций при перерисовке.
  2. Использовать requestAnimationFrame для крупных списков при обновлении элементов.
  3. Минимизировать сложные вычисления в обработчиках событий onMove и onEnd.

Стилизация и пользовательский опыт

  • ghostClass и chosenClass позволяют создавать визуальные эффекты при перетаскивании.
  • Подсветка элементов и плавная анимация увеличивают отзывчивость интерфейса.
  • Кастомизация handle и draggable улучшает контроль над поведением элементов.

Интеграция с фреймворками

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