бесконечный цикл обновления в Vue

1

Я пытаюсь написать пользовательский компонент. И надеюсь, что я смогу использовать его так

let app = new Vue({
    el:'#app',
    template:'
    <tab>
    <tab-item name='1'>
    <h1> This is tab item 1</h1>
    </tab-item>
    <tab-item name='2'>
    <h2> This is tab item 2</h2>
    </tab-item>
    </tab>',
    components:{
        tab,
        tabItem
    }
})

Все идет хорошо, пока вы не нажмете кнопку. Я получил сообщение об ошибке с консоли:

[Vue warn]: You may have an infinite update loop in a component render function.

found in

---> <Tab>
       <Root>

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

Как я могу победить эту проблему?

Вот мой код:

let tabItem = {
    props:{
        name:{
            type: String,
            required: true
        }
    },
    render(h){
        let head = this.$slots.head || ''
        let body = this.$slots.default
        let tail = this.$slots.tail || ''
        return h('div', [ 
            h('div', head),
            h('div', body),
            h('div', tail)])
    }
}

let tab = {

    data(){
        return {
            items:'',
            currentView:0
        }
    },
    methods:{
        handleTabClick(item){
            return ()=>{
                let index = this.items.indexOf(item)
                this.currentView = this.items[index]
            }
        },
        extractProps(vnode){
            return vnode.componentOptions.propsData
        }
    },
    render(h){
        this.items = this.$slots.default.filter( node => {
            return /tab-item/.test(node.tag)
        })
        let headers = this.items.map( item => {
            let name = this.extractProps(item).name
            return h('button', {
                on:{
                    click: this.handleTabClick(item)
                }
            }, name)
        })
        let head = h('div', headers)
        this.currentView = this.items[0]
        return h('div',[head, this.currentView])
    }
}

Или любые другие способы реализации этого компонента?

Большое спасибо за то, что помогли мне выйти из ада.

Спасибо за ваш ответ, мои друзья. Я уверен, что я получаю бесконечную ошибку цикла от консоли, и мой код работает не так, как ожидалось. Я не думаю, что использование vnode - хороший способ реализовать этот компонент. Однако это лучшее решение, которое я могу найти.

Эта tab компонент должна определять своего дочернего tabItem, имя которого - tabItem, который также является компонентом. И tab может извлечь некоторые данные из tabItem. В моем случае, tab извлечет name свойство tabItemn, который будет использоваться для создания кнопок для переключения контента. Нажмите кнопку, чтобы перейти к соответствующему контенту, который является телом tabItem. В моем коде это currenView.

Как и в известной библиотеке пользовательского интерфейса, Element, компонент tab можно использовать следующим образом:

<el-tabs v-model="activeName" @tab-click="handleClick">
  <el-tab-pane label="User" name="first">User</el-tab-pane>
  <el-tab-pane label="Config" name="second">Config</el-tab-pane>
  <el-tab-pane label="Role" name="third">Role</el-tab-pane>
  <el-tab-pane label="Task" name="fourth">Task</el-tab-pane>
</el-tabs>

Мне нужно реализовать один такой компонент, но мой будет более простым. Чтобы узнать, как это сделать, я прочитал его исходный код. Может быть, нет хорошего способа фильтровать дочерние компоненты. В источнике они используют это для фильтрации компонента el-tab-pane:

  addPanes(item) {
    const index = this.$slots.default.filter(item => {
      return item.elm.nodeType === 1 && /\bel-tab-pane\b/.test(item.elm.className);
    }).indexOf(item.$vnode);
    this.panes.splice(index, 0, item);
  }

Исходный код

Я знаю, что я могу использовать $children для доступа к своим дочерним компонентам, но это не гарантирует порядок дочерних компонентов, чего я не хочу. Потому что порядок переключения очень важен. Подробные сообщения о vnode не содержатся в документе. Мне нужно прочитать источник.

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

Я, наконец, не решил эту ошибку и признал, что использование такого редкого кода отстой. Но я не знаю других решений. Поэтому мне нужны вы, ребята, помогите.

Благодарю.

  • 0
    Конечно, у вас есть бесконечный цикл. Ваша функция рендеринга всегда вызывает себя. Что вы пытаетесь сделать, и зачем вам нужны функции рендеринга слотов, vnode и hand-roll (экзотические, потенциально сложные и редко используемые)?
  • 0
    @bbsimonbb да, я тоже думаю, что это отстой, но я не могу найти другой способ сделать это .. Я добавляю больше информации.
Теги:
vue.js
vue-component

1 ответ

1

Вы не должны изменять свои данные в функции рендеринга, это неправильно

this.items = this.$slots.default.filter( node => {
  return /tab-item/.test(node.tag)
})

потому что он будет продолжать повторный рендеринг, вот рабочий пример для вашего кода, я просто удалил свойство items из данных и добавил новое вычисляемое свойство items которое возвращает узлы tab-item.

let tab = {

    data(){
        return {
            currentView:0
        }
    },
    methods:{
        handleTabClick(item){
            return ()=>{
                let index = this.items.indexOf(item)
                this.currentView = this.items[index]
            }
        },
        extractProps(vnode){
            return vnode.componentOptions.propsData

        }
    },
    computed: {
    	items(){
      	return this.$slots.default.filter( node => {
            return /tab-item/.test(node.tag)
        })
      }
    },
    render(h){
        
        let headers = this.items.map( item => {
            let name = this.extractProps(item).name
            return h('button', {
                on:{
                    click: this.handleTabClick(item)
                }
            }, name)
        })
        
        
        let head = h('div', headers)
        
        this.currentView = this.items[0]
        
        return h('div',[head, this.currentView])
    }
}

let tabItem = {
		name:"tab-item",
    props:{
        name:{
            type: String,
            required: true
        }
    },
    render(h){
        let head = this.$slots.head || ''
        let body = this.$slots.default
        let tail = this.$slots.tail || ''
        return h('div', [[ 
            h('div', head),
            h('div', body),
            h('div', tail)]])
    }
}


let app = new Vue({
    el:'#app',
    template:'
    <tab>
    <tab-item name='1'>
    <h1> This is tab item 1</h1>
    </tab-item>
    <tab-item name='2'>
    <h2> This is tab item 2</h2>
    </tab-item>
    </tab>',
    components:{
        tab,
        tabItem
    }
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>  

<div id="app"></div>
  • 0
    Спасибо приятель. Ваше решение остановило предупреждение с консоли. Тем не менее, он не может работать так, как ожидалось. При нажатии кнопки содержимое не переключалось. Но вы найдете ключ, чтобы решить это. Большое спасибо.
  • 0
    this.currentView = this.items[0] касается изменения содержимого при нажатии кнопок, вам нужно изменить this.currentView = this.items[0] , изменить индекс для динамического выбора необходимого содержимого,
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню