Vue中的无限更新循环

时间:2018-02-07 14:26:50

标签: javascript vue.js vue-component

我正在尝试编写自定义组件。希望我能像这样使用它

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的子项,该子项也是一个组件。 tab可以从tabItem中提取一些数据。在我的情况下,tab将提取name的{​​{1}}属性,该属性将用于生成切换内容的按钮。单击按钮可以切换到相关内容,即tabItemn的正文。在我的代码中,它是tabItem

与着名的UI库Element一样,其currenView组件可以像这样使用:

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

Source Code

我知道我可以使用 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的详细消息未包含在文档中。我需要阅读来源。

因此,在阅读了Vue的源代码后,我编写了这样的代码,然后我遇到了问题。

我终于没有解决这个错误,并承认使用这种罕见的代码很糟糕。但我不知道其他解决方案。所以我需要你们的帮助。

感谢。

1 个答案:

答案 0 :(得分:2)

You shouldn't change your data in render function,这是错误的

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

因为它将继续重新呈现,这里是代码的一个工作示例,我只是从数据中删除了items属性并添加了新的items计算属性,该属性返回tab-item个节点。

&#13;
&#13;
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
    }
})
&#13;
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.min.js"></script>  

<div id="app"></div>
&#13;
&#13;
&#13;