效果:
index.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>标签页组件</title>
<style>
[v-cloak]{
display: none;
}
.tabs{
font-size:14px;
color:#657180;
}
.tabs-bar:after{
content: '';
display: block;
width:100%;
height:1px;
background:#d7dde4;
margin-top:-1px;
}
.tabs-tab{
display: inline-block;
padding:5px 10px;
border:1px solid #d7dde4;
margin:0 10px;
}
.tabs-tab-active{
color:#3399ff;
border-top:2px solid #3399ff;
border-bottom:1px solid #fff;
}
.tabs-tab-active:before{
content:'';
display: inline;
height:1px;
background:#3399ff;
position: absolute;
top:0;
left:0;
right:0;
}
.tabs-content{
padding: 8px 0;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<tabs v-model="activeKey">
<pane label='标签一' name="1">
<h1>ReactNative </h1>
<p>使用JavaScript和React编写原生移动应用</p>
</pane>
<pane label='标签二' name="2">
<p>重磅 | iView 发布 3.0 版本,以及开发者社区等 5 款新品</p>
</pane>
<pane label='标签三' name="3">
<h6>
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
</h6>
</pane>
</tabs>
</div>
<script src = "../lib/vue.min.js" ></script>
<script src = "pane.js" ></script>
<script src = "tabs.js" ></script>
<script>
var app = new Vue({
el: '#app',
data: {
activeKey: '1'
}
})
</script>
</body>
</html>
tabs.js
Vue.component('tabs',{
name: 'pane',
template:`
<div class="tabs">
<div class="tabs-bar">
<!-- 标签页标题,这里要用 v-for -->
<div :class='tabCls(item)' v-for="(item,index) in navList" :key='index' @click="handleChange(index)">
{{item.label}}
</div>
</div>
<div class="tabs-content">
<!-- 这里的slot就是嵌套的pane -->
<slot></slot>
</div>
</div>
`,
props:{
value :{type:[String,Number]}
},
data(){
return{
//用于渲染 tabs 的标题
navList: [],
//因为不能修改 value,所以复制一份自己维护
currentValue: this.value,
}
},
methods:{
tabCls(item){
return[
'tabs-tab',
{
//给当前选中的tab 加一个 class
'tabs-tab-active': item.name === this.currentValue
}
]
},
handleChange(index){
var nav = this.navList[index];
var {name} = nav;
//改变当前选中的tab,并触发下面的 watch
this.currentValue = name;
//更新 value
this.$emit('input', name);
//触发一个自定义事件,供父级使用
this.$emit('on-click', name);
},
getTabs(){
//通过遍历子组件,得到所有的 pane 组件
return this.$children.filter((item)=>{
return item.$options.name === 'pane';
})
},
updateNav(){
this.navList = [];
this.getTabs().forEach((pane, index)=>{
this.navList.push({
label: pane.label,
name: pane.name || index
})
//如果没有给 pane 设置 name,默认设置它的索引
if(!pane.name) pane.name = index;
//设置当前选中的tab 的索引,
if(index === 0){
if(!this.currentValue){
this.currentValue = pane.name || index;
}
}
})
this.updateStatus();
},
updateStatus(){
var tabs = this.getTabs();
tabs.forEach((tab)=>{
return tab.show = tab.name === this.currentValue;
})
}
},
watch:{
value(val){
this.currentValue = val;
},
currentValue(){
//在当前选中的 tab 发生变化时,更新pane的显示状态
this.updateStatus();
}
}
})
pane.js
Vue.component('pane',{
name: 'pane',
template:`
<div class="pane" v-show="show">
<slot></slot>
</div>
`,
props:{
name: {type: String},
label: {type: String, default: ''}
},
data(){
return{
show: true,
}
},
methods: {
updateNav(){
this.$parent.updateNav();
}
},
watch:{
label(){
this.updateNav();
}
},
mounted() {
this.updateNav();
},
})