Vue中的无限更新循环

Posted

技术标签:

【中文标题】Vue中的无限更新循环【英文标题】:infinite update loop in Vue 【发布时间】:2018-07-17 21:17:12 【问题描述】:

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

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 将提取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 的源代码之后,我这样编写了我的代码,然后我遇到了问题。

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

谢谢。

【问题讨论】:

肯定你有一个无限循环。你的渲染函数总是调用它自己。你想做什么,为什么需要槽、vnode 和手动渲染函数(这些函数很奇特、可能很复杂并且很少使用)? @bbsimonbb 是的,我也认为这很糟糕,但我找不到其他方法。我添加更多信息。 【参考方案1】:

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

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>

【讨论】:

谢谢老兄。您的解决方案确实停止了来自控制台的警告。但是,它无法按预期运行。单击按钮时,内容没有切换。但是你找到了解决它的关键。非常感谢。 点击按钮时改变内容,需要改变this.currentView = this.items[0]这一行,改变索引动态选择需要的内容,

以上是关于Vue中的无限更新循环的主要内容,如果未能解决你的问题,请参考以下文章

Vue中的无限更新循环

Vue 组件中的“组件渲染函数中可能存在无限更新循环”警告

如何可视化vue无限更新循环的无限循环

控制台上的 Vue 警告:组件渲染函数中可能存在无限更新循环

Vue.js 警告您可能在组件渲染函数中有无限更新循环

如何修复或抑制误报“您可能在组件渲染函数中有无限更新循环”Vue 警告