这篇文章是这个系列的第二篇文章,主要和大家聊一下Element中的折叠面板组件的源码,学习一下看el-collapse是如何是实现
collapse组件的src文件夹下主要分为两个文件,以及对应的index。
collapse.vue文件解析
<template>
<div class="el-collapse" role="tablist" aria-multiselectable="true">
<!-- slot 包裹子组件 collapse-item-->
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ElCollapse',
componentName: 'ElCollapse',
props: {
// 设置是否以手风琴模式显示。
accordion: Boolean,
//
value: {
type: [Array, String, Number],
default() {
return [];
}
}
},
data() {
return {
// 激活的面板数组
activeNames: [].concat(this.value)
};
},
provide() {
return {
collapse: this
};
},
watch: {
value(value) {
this.activeNames = [].concat(value);
}
},
methods: {
setActiveNames(activeNames) {
// 深拷贝,返回activeNames的副本
activeNames = [].concat(activeNames);
// 根据 accordion判断,如果 accordion 为true的话则为手风琴效果返回activeNames[0],不是则返回整个数组
let value = this.accordion ? activeNames[0] : activeNames;
// 将副本赋值给数据源
this.activeNames = activeNames;
// 抛出input方法,并将对应的value传出去
this.$emit('input', value);
// 抛出input方法,并将对应的value传出去
this.$emit('change', value);
},
handleItemClick(item) {
// 首先判断是不是手风琴,如果是手风琴
if (this.accordion) {
// 调用setActiveNames()函数,给value设置新的值,对应的操作就是打开新的,关闭旧的
this.setActiveNames(
(this.activeNames[0] || this.activeNames[0] === 0) &&
this.activeNames[0] === item.name
? '' : item.name
);
// 不是手风琴
} else {
// 获取以及激活的面板数组
let activeNames = this.activeNames.slice(0);
let index = activeNames.indexOf(item.name);
// 判断该面板是否已经激活,
if (index > -1) {
// 如果已经激活,就将其从激活的面板数组中删除
activeNames.splice(index, 1);
} else {
// 如果没有激活就将起push到激活的面板数组
activeNames.push(item.name);
// 对应的交互就是如果面板没有打开,点击就能打开,如果面板打开了,点击就会关闭
}
// 重新给activeNames 进行赋值
this.setActiveNames(activeNames);
}
}
},
created() {
// 监听子组件抛出的 item-click 事件,并触发 handleItemClick 函数
this.$on('item-click', this.handleItemClick);
}
};
</script>
collapse-item.vue文件解析
<template>
<div class="el-collapse-item"
:class="{'is-active': isActive, 'is-disabled': disabled }">
<div
role="tab"
:aria-expanded="isActive"
:aria-controls="`el-collapse-content-${id}`"
:aria-describedby ="`el-collapse-content-${id}`"
>
<!-- 面板的头部 -->
<div
class="el-collapse-item__header"
@click="handleHeaderClick"
role="button"
:id="`el-collapse-head-${id}`"
:tabindex="disabled ? undefined : 0"
@keyup.space.enter.stop="handleEnterClick"
:class="{
'focusing': focusing,
'is-active': isActive
}"
@focus="handleFocus"
@blur="focusing = false"
>
<!-- 面板标题 通过具名slot可以实现自定义-->
<slot name="title">{{title}}</slot>
<!-- 是否折叠的icon图标 -->
<i
class="el-collapse-item__arrow el-icon-arrow-right"
:class="{'is-active': isActive}">
</i>
</div>
</div>
<!-- 内容区域的动画效果 -->
<el-collapse-transition>
<div
class="el-collapse-item__wrap"
v-show="isActive"
role="tabpanel"
:aria-hidden="!isActive"
:aria-labelledby="`el-collapse-head-${id}`"
:id="`el-collapse-content-${id}`"
>
<div class="el-collapse-item__content">
<!-- 面板的内容区域采用具名槽口,根据使用者输入进行展示 -->
<slot></slot>
</div>
</div>
</el-collapse-transition>
</div>
</template>
<script>
import ElCollapseTransition from 'element-ui/src/transitions/collapse-transition';
import Emitter from 'element-ui/src/mixins/emitter';
//随机产生id的公共函数
import { generateId } from 'element-ui/src/utils/util';
export default {
name: 'ElCollapseItem',
componentName: 'ElCollapseItem',
mixins: [Emitter],
components: { ElCollapseTransition },
data() {
return {
contentWrapStyle: {
height: 'auto',
display: 'block'
},
contentHeight: 0,
focusing: false,
isClick: false,
// 获取随机的id
id: generateId()
};
},
inject: ['collapse'],
props: {
title: String,
name: {
type: [String, Number],
default() {
return this._uid;
}
},
disabled: Boolean
},
computed: {
// 返回当前面板是否被激活
isActive() {
return this.collapse.activeNames.indexOf(this.name) > -1;
}
},
methods: {
handleFocus() {
setTimeout(() => {
if (!this.isClick) {
this.focusing = true;
} else {
this.isClick = false;
}
}, 50);
},
// 面板头部的点击事件,返回给父组件
handleHeaderClick() {
// 如果是禁止点击 直接返回
if (this.disabled) return;
// 将折叠m面板的点击事件抛出给父组件
this.dispatch('ElCollapse', 'item-click', this);
this.focusing = false;
this.isClick = true;
},
handleEnterClick() {
// 将折叠m面板的点击事件抛出给父组件
this.dispatch('ElCollapse', 'item-click', this);
}
}
};
</script>
src 中utils文件夹中的util.js 随机生成id 的函数 ,随机生成的id在vue中是比较重要,相当于每一个元素节点都有一个唯一不重复的标识,这样不管是在页面渲染还是更新的时候都可以大大提升性能。
export const generateId = function() {
return Math.floor(Math.random() * 10000);
};
今天主要看了collapse的源代码,以及实现的过程,有些收获,继续加油,奥里给