githup地址:https://github.com/dubuxunqi/vue-tabs
1.首先写index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>选项卡</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="app" v-cloak>
<!-- @input接收子组件传来的name -->
<tabs v-model="value" @input="tabData">
<pane label="标签一" name="1">
标签一的内容
</pane>
<pane label="标签二" name="2">
标签二的内容
</pane>
<pane label="标签三" name="3">
标签三的内容
</pane>
</tabs>
</div>
<script type="text/javascript" src="vue.js"></script>
<script type="text/javascript" src="pane.js"></script>
<script type="text/javascript" src="tabs.js"></script>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
// 初始显示第几个
value:"3"
},
methods:{
tabData:function(item){
console.log(item);
}
}
})
</script>
</body>
</html>
2.我们需要两个组件,tabs组件和pane组件,pane组件嵌套在tabs组件里面,pane组件中slot内放业务逻辑代码(代码里面都有详细的注释,这里就不在一一解释)
首先看下pane组件内容:
Vue.component('pane',{
name:"pane",
template:'\
<div class="pane" v-show="show">\
<slot></slot>\
</div>',
props:{
// 用来标识点击的哪个标签
name:{
type:String
},
// 标签的标题内容
label:{
type:String,
default:""
}
},
data:function(){
// show控制显示哪个内容
return {
show:true
}
},
methods:{
// this.$parent访问父组件中的方法,注意在业务代码中尽量不要使用$parent来操作父链,这种方法试用于标签这样独立的组件
updateNav(){
this.$parent.updateNave();
}
},
watch:{
// 监听label,label变化时,调用updateNav
label(){
this.updateNav();
}
},
mounted(){
// mounted生命周期钩子同样调用updateNav
this.updateNav();
}
})
tabs组件的内容:
Vue.component('tabs',{
template:'\
<div class="tabs">\
<div class="tabs-bar">\
<!--标题页的标题 v-for遍历, :class 动态绑定class-->\
<div \
:class="tabCls(item)"\
v-for="(item,index) in navList"\
@click="handleChange(index)">\
{{item.label}}\
</div>\
</div>\
<div class="tabs-content">\
<!-- 这里的slot是嵌套pane组件 -->\
<slot></slot>\
</div>\
</div>',
props:{
value:{
//接收父组件的value
type:[String]
}
},
data:function(){
return {
//保存父组件的value到currentValue变量中,以便在本地维护
currentValue:this.value,
//将pane的标题保存到数组中
navList:[]
}
},
methods:{
tabCls:function(item){
//为当前选中的tab加一个tabs-tab-active class
return [
'tabs-tab',
{
'tabs-tab-active':item.name === this.currentValue
}
];
},
getTabs:function(){
//使用$children遍历子组件,得到所有的pane组件
return this.$children.filter(function(item){
return item.$options.name === 'pane';
});
},
//更新tabs
updateNave:function(){
this.navList = [];
// foreach里面的回调里的this不再是tabs组件本身,所以设置_this=this
var _this= this;
this.getTabs().forEach(function(pane,index){
_this.navList.push({
label:pane.label,
name:pane.name || index
});
//如果没有设置name,默认设置为索引值
if(!pane.name) pane.name = index;
//设置第一个pane为当前显示的tab
if(index === 0){
if(!_this.currentValue){
_this.currentValue = pane.name || index;
}
}
});
this.updateStatus();
},
updateStatus:function(){
var tabs = this.getTabs();
var _this = this;
//显示当前选中的tab对应的pane组件,隐藏没有选中的
tabs.forEach(function(tab){
return tab.show = tab.name ===_this.currentValue;
});
},
//点击tab标题触发
handleChange:function(index){
var nav = this.navList[index];
var name = nav.name;
//改变当前选中的tab,触发watch
this.currentValue = name;
//实现子组件与父组件通信
this.$emit('input',name);
}
},
watch:{
// 监听value变化
value:function(val){
this.currentValue = val;
},
// 监听currentValue变化,更新对应的pane组件
currentValue:function(){
this.updateStatus();
}
}
})
有什么不明白的,欢迎评论,我会解答,或是来拍砖,我们一起学习。