我正在使用vue创建选项卡组件,并注意到以下行为。
如果我这样创建我的组件:
<tabs-component>
<tab-component :title="1">
<h1>1</h1>
<p>Lorem ipsum dolor sit amet.</p>
</tab-component>
<tab-component :title="2">
<h1>2</h1>
<p>Lorem ipsum dolor sit amet.</p>
</tab-component>
</tabs-component>
救生钩按以下顺序触发:
但是,这似乎是正确的行为,如果我不是在手动声明插槽,而是使用v-for
指令,假设我正在处理来自以下服务的某些数据:
<tabs-component>
<tab-component v-for="(type, index) in beerTypes" :key="type.slug" :title="type.slug">
<h1>{{ index }}</h1>
<p>Lorem ipsum dolor sit amet.</p>
</tab-component>
</tabs-component>
救生钩按以下顺序触发:
v-for
指令似乎与生命挂钩的触发顺序混乱。这引起了我的麻烦,因为我在已安装的救生钩上触发了selectTab
方法来选择所选的默认选项卡,但在触发时尚未安装选项卡。
有人知道我如何以正确的顺序正确地监听事件,或者等待子组件准备好以选择默认面板吗?
这是我的其余代码供参考:
App.vue
<template>
<main>
<h1>Discover beer styles</h1>
<tabs-component>
<tab-component v-for="(type, index) in beerTypes" :key="type.slug" :title="type.slug">
<h1>{{ index }}</h1>
<p>Lorem ipsum dolor sit amet.</p>
</tab-component>
</tabs-component>
</main>
</template>
<script>
import axios from 'axios';
import TabsComponent from './components/tabs/Tabs.vue';
import TabComponent from './components/tabs/Tab.vue';
export default {
components: {
'tabs-component': TabsComponent,
'tab-component': TabComponent,
},
data() {
return {
beerTypes: null,
selectedType: null
}
},
filters: {
capitalize: function(value) {
if (!value) return '';
return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
}
},
mounted() {
axios.get('/data/data.json')
.then((response) => {
this.beerTypes = response.data;
this.selectedType = this.beerTypes[0].slug;
})
.catch((error) => console.log(error));
},
methods: {
selectActiveType: function(selected) {
this.selectedType = selected;
}
}
}
</script>
Tabs.vue
<template>
<div class="tabs-component">
<div>
<ul>
<li v-for="(tab, index) in tabs" :class="{'is-active': tab.isActive}" :key="index">
<a href="#" @click="selectTab(index)">{{ tab.title }}</a>
</li>
</ul>
</div>
<div>
<slot/>
</div>
</div>
</template>
<script>
export default {
data() {
return {
tabs: [],
activeTabIndex: 0
}
},
created() {
this.tabs = this.$children;
},
mounted() {
this.selectTab(this.activeTabIndex);
},
methods: {
selectTab(index) {
this.tabs.forEach(function(tab) {
tab.isActive = (this.tabs.indexOf(tab) === index);
}.bind(this));
}
}
};
</script>
Tab.vue
<template>
<section v-show="isActive">
<slot/>
</section>
</template>
我很高兴有人能抽出时间来帮助我。
谢谢!
答案 0 :(得分:1)
我认为使用Vue: provide/inject是这里的一种解决方案。
对于您的情况,如果添加了新的<tab-component>
(您说啤酒类型将从后端服务进行更新),除非在hook = {{内获取最新的<tabs-component>
,否则this$children
不会知道1}}。
如果使用提供/插入,我们可以让updated()
将其实例注册到<tab-component>
的一个数据属性中。这样<tabs-components>
就不必在意添加新标签了,并且可以直接访问<tabs-component>
的实例。
此外,您还应该实现一个<tab-component>
,实例销毁后,将其从_unRegister
的data属性中删除,下面的演示使用hook = {<tabs-component>
来实现。
下面是一个dmeo:
beforeDestory
Vue.productionTip = false
Vue.component('tabs-component', {
template: ` <div class="tabs-component">
<div>
<h3>Selected Tab: {{activeTabIndex}}</h3>
<ul>
<li v-for="(tab, index) in tabs" :class="{'is-active': tab.isActive}" :key="index">
<a href="#" @click="selectTab(index)">{{ tab.title }}</a>
</li>
</ul>
</div>
<div>
<slot/>
</div>
</div>`,
provide () {
return {
_tabs: this
}
},
data() {
return {
tabs: [],
activeTabIndex: 0
}
},
mounted() {
},
methods: {
selectTab(selectedIndex) {
this.activeTabIndex = selectedIndex
this.tabs.forEach((tab, tabIndex) => {
tab.isActive = selectedIndex === tabIndex
})
},
_registerTab(tab) {
this.tabs.push(tab)
this.selectTab(this.activeTabIndex)
},
_unRegisterTab(unRegTab) {
this.tabs = this.tabs.filter(tab=>{
return unRegTab !== tab
})
this.selectTab(0)
}
}
})
Vue.component('tab-component', {
template: ` <section v-show="isActive" :title="title">
<slot/>
</section>`,
inject: {
_tabs: {
default () {
console.error('tab-component must be child of tab-components')
}
}
},
props: ['title'],
mounted () {
this._tabs._registerTab(this)
},
beforeDestroy(){
this._tabs._unRegisterTab(this)
},
data () {
return {
isActive: false
}
}
})
new Vue({
el: '#app',
data () {
return {
beerTypes: []
}
},
methods: {
pushData () {
setTimeout(()=>{
this.beerTypes.push({'slug': 'test' + this.beerTypes.length})
}, 1000)
},
popData () {
this.beerTypes.pop()
}
}
})
.is-active {
background-color: green;
}
并在Vue指南中的提供/注入上注意此注释:
注意:Provide和Inject绑定不是反应性的。这是 故意的。但是,如果您向下传递观察对象,则属性 在那个物体上确实保持反应性。