我正在尝试编写自定义组件。希望我能像这样使用它
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
我知道我可以使用 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的源代码后,我编写了这样的代码,然后我遇到了问题。
我终于没有解决这个错误,并承认使用这种罕见的代码很糟糕。但我不知道其他解决方案。所以我需要你们的帮助。
感谢。
答案 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
个节点。
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;