Element组件库源码学习系列2(探究el-collapse)

姬实
2023-12-01

前言

这篇文章是这个系列的第二篇文章,主要和大家聊一下Element中的折叠面板组件的源码,学习一下看el-collapse是如何是实现

一、框架分析

collapse组件的src文件夹下主要分为两个文件,以及对应的index。

  • collapse.vue
  • collapse-item.vue

二、源码分析

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的源代码,以及实现的过程,有些收获,继续加油,奥里给

 类似资料: