javascript - Vue 中的无限更新循环

标签 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])
    }
}

或者任何其他方式来实现这个组件?

非常感谢您帮助我摆脱困境。

谢谢 friend 们的回复。我很确定我从控制台收到无限循环错误并且我的代码没有按预期工作。我认为使用 vnode 也不是实现此组件的好方法。但是,这是我能想到的最佳解决方案。

此组件 -- tab 应检测其名称为 tabItem 的子项,这也是一个组件。而 tab 可以从 tabItem 中提取一些数据。在我的例子中,tab 将提取 tabItemnname 属性,该属性将用于生成用于切换内容的按钮。点击按钮可以切换到相关内容,即tabItem的主体。在我的代码中,它是 currenView

像一个著名的UI库,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);
  }

Source Code

我知道我可以使用 $children 来访问它的子组件,但是这样做并不能保证子组件的顺序,这不是我想要的。因为切换按钮的顺序很重要。有关 vnode 的详细消息未包含在文档中。我需要阅读源代码。

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

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

谢谢。

最佳答案

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 节点。

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/vue@2.5.13/dist/vue.min.js"></script>  

<div id="app"></div>

关于javascript - Vue 中的无限更新循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48666240/

相关文章:

javascript - PHP 和 JavaScript

javascript - 如何禁用带有单选按钮的组合框(javascript)

javascript - 使用键盘控制在 Canvas 游戏中平滑角色移动

reactjs - 如何在 momentjs 中获取 endOf day toISOString() 但毫秒应该是 .000Z 而不是 .999Z

javascript - Vuex:如何定义一个状态变量,其值基于另一个状态变量的值?

twitter-bootstrap - Bootstrap-Vue 中按列对 <b-table> 进行排序并禁止用户排序

javascript - 不使用 Flash 实现与 ctrl+c 相同的 ctrl+x 行为

vue.js - vuejs : router-link params is empty

javascript - 如何在 vuetify 中创建可滚动的 v-list?

laravel - vee-validate 中的自定义验证消息