Vue: удаление элемента из списка v-for оставляет за собой элемент canvas

#javascript #vue.js #html5-canvas

#javascript #vue.js #html5-canvas

Вопрос:

Это сложно сформулировать в заголовке, но довольно легко объяснить с помощью некоторых настроек.

У меня есть список компонентов. Каждый компонент в списке выглядит следующим образом:

 | [Canvas Preview] Name      (Delete button) |
  

Каждый компонент имеет небольшой элемент canvas, который используется для отображения предварительного просмотра элемента, его имени и кнопки удаления, чтобы удалить его из списка. Список, по которому выполняется итерация v-for, хранится в Vuex.

Используя буквы для представления разных изображений предварительного просмотра, список может выглядеть следующим образом:

 | [W] Item 1      (Delete button) |
| [X] Item 2      (Delete button) |
| [Y] Item 3      (Delete button) |
| [Z] Item 4      (Delete button) |
  

При нажатии кнопки удаления соответствующий элемент удаляется из списка, и список обновляется. Имена обновляются, и щелчок по ним приводит к выбору правильного элемента. Проблема в том, что предварительные просмотры остаются в том же положении. Например, если бы я удалил элемент 2 (с предварительным просмотром X) из списка выше, у меня был бы следующий список:

 | [W] Item 1      (Delete button) |
| [X] Item 3      (Delete button) |
| [Y] Item 4      (Delete button) |
  

Предварительные просмотры являются WXY, когда они должны быть WYZ, поскольку X был удален. В основном холсты остаются в том же порядке, и один просто удаляется с конца, независимо от того, откуда был удален элемент.

Я могу придумать пару хакерских способов перерисовки всех холстов при удалении элемента, но мне было интересно, есть ли лучшее решение.

РЕДАКТИРОВАТЬ: Вот код, который удаляет элемент

Кнопка удаления на элементе отправляет событие в компонент списка

 <button @click="deleteAsset">Delete</button>

deleteAsset(event){
    event.stopPropagation();
    this.isRenaming = false;
    this.$emit('deleteAsset', this.asset);
},
  

Затем оболочка отправила действие Vuex

 deleteAsset(asset){
    this.$store.dispatch('GameData/deleteAsset', {category: asset.category_ID, id: asset.ID});
    this.updateAsset();
},
  

Действие Vuex и мутация

 //Action
deleteAsset({commit}, {category, id}){
    commit('deleteAsset', {category, id})
}

//Mutation
deleteAsset: (state, {category, id}) => {
    let curList = getList();
    let hasFound = false;

    for (let i = 0; !hasFound amp;amp; i < curList.length; i  ){
        if (curList[i].ID == id){
            curList.splice(i, 1);
            hasFound = true;
        }
    }
}
  

Вот скриншот проблемы:
Скриншот проблемы с Vue

Остальные данные верны, так как я добавил функцию быстрого тестирования для печати текущего идентификатора и данных по щелчку, и все печатается правильно. Похоже, что это просто элемент canvas, который не меняется.

ПРАВКА 2: код vue

 //List
<Asset
    ref="assets"
    v-for="asset in selectedList"
    :key="asset.cat_ID"
    :asset="asset"
    :defaultIcon="selected_category.icon"
    @deleteAsset="deleteAsset"
    @selectAsset="selectAsset"/>

//Asset code
<template>
    <div ref="asset" class="asset" :class="{selected : isSelected}" @click="selectAsset">
        <div class="leftFloat">
            <canvas v-show="hasThumb" class="thumbnail" ref="thumbNail" width="20" height="20">Test</canvas>
            <img v-if="!hasThumb" class="thumbnail assetIcon" :src="require(`@/${defaultIcon}.svg`)" />
            <div v-if="isRenaming">
                <input ref="renameText" v-model="asset.name" type="text" />
            </div>
            <div v-else>{{asset.name}}</div>
        </div>
        <div class="rightFloat">
            <button class="rightButton" @click="deleteAsset">
                <img class="rightIcon" src="@/assets/trash.svg" />
            </button>
        </div>
    </div>
</template>
  

Комментарии:

1. пожалуйста, добавьте коды, как вы удаляете элемент из списка.

2. Вероятно, это :key проблема. Как вы отображаете данные?

3. Вот и все! Я использовал идентификатор категории ( asset.cat_id ) вместо идентификатора ресурса ( asset.ID ) для ключа. Раньше у меня были проблемы с Android и другими подобными фреймворками, где у него были проблемы с «повторным использованием», поэтому я предположил, что это аналогичная проблема. Странно, как имена обновлялись правильно, хотя никогда бы не догадался, что это была проблема : key. Огромное спасибо

Ответ №1:

Предполагая, что cat_ID может быть назначен более чем одному ресурсу, вы столкнетесь с проблемами, используя его как key поскольку Vue не сможет обнаружить изменения списка, если cat_ID повторяется в любом из ваших selectedList активов.

Поскольку у ваших активов есть ID свойство, вы должны использовать его в своем key

 <Asset
  ref="assets"
  v-for="asset in selectedList"
  :key="asset.ID"
  :asset="asset"
  :defaultIcon="selected_category.icon"
  @deleteAsset="deleteAsset"
  @selectAsset="selectAsset"/>
  

Смотрите key

key Специальный атрибут в основном используется как подсказка для алгоритма виртуального DOM Vue для идентификации VNodes при сравнении нового списка узлов со старым списком. Без ключей Vue использует алгоритм, который минимизирует перемещение элементов и пытается как можно больше исправлять / повторно использовать элементы того же типа на месте. С помощью keys он изменит порядок элементов на основе изменения порядка ключей, а элементы с ключами, которые больше не присутствуют, всегда будут удалены / уничтожены.

Комментарии:

1. Именно это, спасибо! Поскольку имена менялись, это действительно сбивало меня с толку, и я никогда бы не догадался, что это :key проблема