好久没有写文章了,这一段时间在忙着准备期末考试和找实习,上周已经在一家公司开始实习了,公司是用Vue开发项目,Boss让我们实习生看几个项目的代码,第一次接触完整的上线项目,发现代码结构分的很细,几乎每个文件都在使用export、import导入导出,有些Vue用法是之前没有接触过的,实习一周下来看代码看的有点蒙。。。所以打算利用空余时间,重新学习Vue,至少一周写一篇Vue实践总结~
Vue作为一个前端轻量级的MVVM框架,组件化是其一个重要的功能和特点,组件化的优点是显而易见的,一个页面的不同部分可以拆分成独立的组件,然后在不同的页面就可以共享这些组件,避免重复开发。下面是我编写的一个标签页组件,先上最终效果图~
![演示图](https://user-gold-cdn.xitu.io/2019/7/7/16bcc09348961dc9?w=435&h=235&f=gif&s=42077 "演示图")
### 组件文件结构
- index.html 入口页
- style.css 样式页
- tab.js 标签页组件 tabs
- pane.js 标签页组件 pane
![文件目录](https://user-gold-cdn.xitu.io/2019/7/7/16bcc09348bf725c?w=312&h=181&f=png&s=7738 "文件目录")
### 初始化各个文件
index.js:
```javascript
<html>
<head>
<meta charset="UTF-8">
<title>标签页组件</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="app" v-cloak>
<tabs v-model="activeKey">
<pane label="标签一" name="1">
标签一的内容
</pane>
<pane label="标签二" name="2" :closable="false">
标签二的内容很重要,不能关闭
</pane>
<pane label="标签三" name="3">
标签三的内容
</pane>
</tabs>
</div>
<script src="https://unpkg.com/vue@2.6.10/dist/vue.min.js"></script>
<script src="pane.js"></script>
<script src="tabs.js"></script>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
activeKey: '1'
}
})
</script>
</body>
</html>
```
tab.js:
```javascript
Vue.component('tabs',{
template:'\
<div class="tabs">\
<div class="tabs-bar">\
<!--标签页的标题,需要使用v-for-->\
<div \
</div>\
</div>\
<div class="tabs-content">\
<!--这里的slot即是嵌套的pane组件-->\
<slot></slot>\
</div>\
</div>',
})
```
pane.js
```javascript
Vue.component('pane',{
name:'pane',
template: '\
<div>\
<slot></slot>\
</div>',
data: function(){
return{
show:true
}
}
})
```
pane需要控制标签页内容的显示与隐藏,设置一个data:show,通过这个属性来动态添加class
### 功能实现
在pane.js里设置`prop: name`唯一的值来标识这个 pane,但它不是必需的,如果使用者不设置,可以默认从0开始自动设置;设置`prop: label`其内容显示在标签页标题里;还有`prop: closable`用来是否显示关闭标签按钮。这部分代码如下:
```javascript
props:{
name: {
type:String
},
label: {
type:String,
default:''
},
closable: {
type: Boolean,
default: true
}
}
```
由于上面的`prop: label`用户是可以动态调整的,所以在pane初始化及label更新时,都要通知父组件更新,更新方法定为`updateNav`
```javascript
methods:{
updateNav (){
this.$parent.updateNav();
}
},
watch:{
label(){
this.updateNav();
}
},
mounted(){
this.updateNav();
}
```
pane.js功能基本实现了,剩余任务就是完成tabs.js组件。</BR>
首先需要把pane组件设置的标题动态谊染出来,也就是当pane触发tabs的updateNav方法时,更新标题内容。这部分的代码:
```javascript
methods: {
getTabs () {
// 通过遍历子组件,得到所有的pane组件
return this.$children.filter(function(item){
return item.$options.name==='pane';
})
},
updateNav () {
this.navList=[];
var _this=this;
this.getTabs().forEach(function(pane,index){
_this.navList.push({
label: pane.label,
name: pane.name||index,
closable: pane.closable
});
if(!pane.name){
pane.name=index;
}
if(index==0){
if(!_this.currentValue){
_this.currentValue=pane.name||index;
}
}
});
this.updateStatus();
},
updateStatus () {
var tabs=this.getTabs();
var _this=this;
// 显示当前选中的tab对应的pane组件
tabs.forEach(function(tab){
return tab.show = tab.name === _this.currentValue;
})
}
}
```
拿到navList后,就需要对它用v-for指令把tab的标题渲染出来,并且判断每个tab当前的状态:是否选择,是否可以关闭。这部分代码如下:
```javascript
Vue.component('tabs',{
template:' \
<div class="tabs"> \
<div class="tabs-bar"> \
<div \
:class="tabCls(item)" \
v-for="(item,index) in navList" \
@click="handleChange(index)">\
{{item.label}} \
<span v-if="ifShowClose(item)" class="close icon" @click.stop="closeTab(index)"></span> \
</div> \
</div> \
<div class="tabs-content"> \
<slot></slot> \
</div> \
</div>'
})
```
上面标签绑定了三个方法:`handleChange`,`ifShowClose`,`closeTab`,其代码如下
```javascript
handleChange: function(index){
var nav=this.navList[index];
var name=nav.name;
// 更新当前选择的tab
this.currentValue=name;
// 更新value
this.$emit('input',name);
},
ifShowClose (item) {
// 是否显示关闭标签按钮
return item.closable;
},
// 点击关闭按钮触发的事件
closeTab (index) {
// console.log(this.navList[index].name, this.currentValue);
// 如果关闭的是当前选择的tab,则将currentValue转到前一个tab
if (this.navList[index].name == this.currentValue) {
let toIndex = index - 1;
toIndex = toIndex >=0 ? toIndex : this.navList.length + toIndex;
console.log(toIndex);
this.currentValue = this.navList[toIndex].name;
}
//关闭当前标签页
this.navList.splice(index, 1);
}
```
另外通过CSS3的`transform: translateX`来增加标签页内容切换动画:
```css
.pane {
visibility: hidden;
width: 100%;
height: 0;
transform: translateX(-100%);
transition: all .5s ease-in;
}
.pane-active {
visibility: visible;
transform: translateX(0);
}
```
### 完整代码
完整代码已经上传到github上了,[传送门](https://github.com/libilin2018/learn-Vue.js)
### 最后
以上是该组件的基本实现,这是我结合《Vue.js实战》这本书完成的,通过这个实例,巩固了Vue的一些基本用法,对Vue的组件化思想有了更清晰的认识。另外我添加的标签切换动画感觉比较生硬,简友们如果有更好的办法欢迎提出来~