我正在尝试编写自定义组件。并希望我能像这样使用它
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
将提取 tabItemn
的 name
属性,该属性将用于生成用于切换内容的按钮。点击按钮可以切换到相关内容,即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);
}
我知道我可以使用 $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/