[Vue warn]: Проблема с рекурсивным компонентом

176
31 августа 2018, 03:40

[Vue warn]: Unknown custom element: <MyTreeList> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

Гуру Vue.js, подскажите, пожалуйста. Пишу компонент сортируемого дерева элементов (как ни странно, подходящего решения не нашел). И столкнулся с проблемой, которую пока не могу обойти. Часы гугления не помогли.
Струтура компонента такая

  • MyTree.vue - главный компонент
  • MyTreeList.vue - список элементов
  • MyTreeItem.vue - непосредственно элемент
  • MyTreeMixin.js
  • utils.js

Каждый компонент наследуется от MyTreeMixin.js и имеет свое свойство name, равное названию файла компонента. В dev режиме (стандартный полный webpack шаблон проекта) ошибка [Vue warn]: Unknown custom element: <MyTreeList> - did you register the component correctly? For recursive components, make sure to provide the "name" option. вылетает через раз. Т.е. сделал правку в MyTreeItem.vue - вебпак пересобрал и с помощью HMR заменил компонент - и компонент отрендерился без ошибок. Внес незначительную правку (просто добавил комментарий или отступ) в MyTreeList.vue - вылезла вышеуказанная ошибка.

// ***********************************************
// MyTreeList.vue
// template
<ul class="tree__list">
  <li is="tree-item"
      v-for="(treeItem, index) of items"
      :key="'index_'+index"
      :item="treeItem"
      :prevItem="index>0 ? items[index-1] : null"
      :isLastChild="index === items.length-1"
      :itemLine="getItemLine(index)"
      :draggable="draggable"
      @item-drag-start="$emit('item-drag-start', $event)"
      @load-children="$emit('load-children', $event)"
      @input="$emit('input', $event)"
      @change-item="$emit('change-item', $event)"
      >
    <template slot-scope="{ treeNode }">
      <slot :treeNode="treeNode">{{ treeNode[stateProps.titleKey] }}</slot>
    </template>
  </li>
</ul>
// script
import Vue from "vue"
import MyTreeMixin from "./MyTreeMixin.js"
import MyTreeItem from "./MyTreeItem.vue"
import { getCoords, throttle } from "./utils.js"
export default Vue.extend({
  name: "My",
  mixins: [MyTreeMixin],
  components:{
    'MyTreeItem': MyTreeItem
  },
  props:{
    list: {
      type: Array,
      required: true
    },
    draggable: {
      type: Boolean,
      default: false
    },
  },
  // ...
})
// ***********************************************
// MyTreeItem.vue
// template
<li class="tree__itemWrap">
  <div class="tree__row">
    <div class="tree__item">
      <div class="tree__content">
        <div>
          <slot :treeNode="treeItem">
            {{ treeItem[stateProps.titleKey] }}
          </slot>
        </div>
      </div>
    </div>
  </div>
  <ul is="MyTreeList"
      v-if="hasChildren"
      v-show="treeItem.__showChildren"
      :list="treeItem.children"
      :draggable="draggable"
      @item-drag-start="$emit('item-drag-start', $event)"
      @load-children="$emit('load-children', $event)"
      @input="$emit('input', $event)"
      @change-item="$emit('change-item', $event)"
  >
    <template slot-scope="{ treeNode }">
      <slot :treeNode="treeNode">
        {{ treeNode[stateProps.titleKey] }}
      </slot>
    </template>
  </ul>
</li>
// script
import Vue from "vue"
import MyTreeMixin from "./MyTreeMixin.js"
import MyTreeList from "./MyTreeList.vue"
import { getCoords, throttle } from "./utils.js"
export default Vue.extend({
  name: "MyTreeItem",
  mixins: [MyTreeMixin],
  components:{
    'MyTreeList': MyTreeList
  },
  props:{
    item: {
      type: Object,
      required: true
    },
    prevItem: {
      type: [Object, null]
    },
    isLastChild: {
      type: Boolean,
      default: false
    },
    itemLine:{
      type: String,
      default: "once",
      validator(value){
        return ['once', 'full', 'first','first-child', 'last'].indexOf(value) !== -1
      }
    },
    draggable: {
      type: Boolean,
      default: false
    },
  },
  computed:{
    // ...
    treeItem(){
      return this.item
    }
  },
  created(){
    // инициализируем вотчеры за каждым свойством элемента,
    // кроме системных свойств и вложенных элементов
    for(const k in this.treeItem){
      if(k !== this.childrenKey && k.indexOf("__") !== 0){
        this.$watch(`treeItem.${k}`, this.onUpdatedItem)
      }
    }
  },
  methods:{
    onUpdatedItem(){
      // при изменении любого примитивного свойства объекта
      // выбрасываем событие с самим элементом
      this.$emit('change-item', this._normalizeItem(this.treeItem))
    },
  },
  // ...
})

В остальных файлах нет ничего интересного и влияющего на проблему.
Проблема именно тогда существует, когда структура включает в себя MyTreeList.vue и MyTreeItem.vue. Заметил, что если избавиться от MyTreeItem.vue и всю логику перенести в MyTreeList.vue, то проблемы с вызовом самого себя рекурсивно не возникает.
Но сейчас важно, чтобы каждый элемент списка был отдельным компонентом, т.к. есть вотчеры (watch), следящие за свойствами элемента

READ ALSO
Удаление EventListener после его вызова

Удаление EventListener после его вызова

Создать сообщение - работаетУдалить сообщение - не работает

171
Улучшить код крестиков-ноликов

Улучшить код крестиков-ноликов

Решил втянуться в ООП, написал простейшие крестики-нолики, но с отсутствием опыта в этой области не знаю, что сделал не так, и как это улучшитьНадеюсь...

157
как переписать на функциональный стиль?

как переписать на функциональный стиль?

Как сделать в функиональном стилеФункция возвращает true, если найдены limit совпадений

163