序言
距离上次发布文章转眼已经快一个月了,最近因为项目的原因比较繁忙,,所有一直断更,但是不要在意这些小细节,步入正题。
按照顺序来说,这次轮到的Select下拉选择器,这个我简单的瞄了一眼的确挺复杂的,所以的话呢 这个select可能要花好几篇来分享这个,反正我看了一下代码 难不难不知道 东西是真的多啊。单单一个select.vue就快1000行了,整体加起来的话可以会冲2000多行吧,但是吧 虽然代码多,但是值得学习的点还是非常的多的。
结构分析
select 组件含有navigation-mixin.js,option-group.vue,option.vue,select-dropdown.vue,select.vue等文件
还是和往常的一样 咱们按照三种形式来分析 但是这次会从多个方面来进行分析,同样也会带着问题来进行看select的代码
因为是想了解的比较透彻一点,所有本章主要内容是分享一些主体的东西
Dom结构
<div class="el-select">
// 多选时用el-tag的形式来展示多选的文字
<div class="el-select__tags">
<span>
// 这边两个tag一个是展示当前多选的第一个
// 另一个是展示 +1 这样的形式展示(官网上 基础多选 第一个的样子)
<el-tag></el-tag>
<el-tag></el-tag>
</span>
// 把每个选中的标签循环展示出来
<transition-group @after-leave="resetInputHeight" v-if="!collapseTags">
<el-tag></el-tag>
</transition-group>
// 可输入文本的查询框
<input type="text" class="el-select__input">
</div>
// 单选时的回显
<el-input ref="reference" v-model="selectedLabel" type="text">
<template slot="prefix" v-if="$slots.prefix">
<slot name="prefix"></slot>
</template>
// 清除和下箭头按钮
<template slot="suffix"> 这里两个小按钮分别用了show和if
<i v-show="!showClose" ></i>
<i v-if="showClose"></i>
</template>
</el-input>
// 下拉菜单
<el-select-menu>
<el-scrollbar>
// 下拉的每个选项
<el-option></el-option>
<slot></slot>
</el-scrollbar>
// 为空时或者loading时的处理
<template>
<slot></slot>
<p></p>
</template>
</el-select-menu>
</div>
复制代码
大致的Dom结构就是这样了,其实吧,只要把外层几个大标签分一下类别,通过对应的class和那些属性的逻辑判断就可以看的出来每个对应的Dom标签是干什么的。
用法功能和问题分析
一 、功能用法
- 基础用法
- 有禁用选项
- 禁用状态
- 可清空单选
- 基础多选
- 自定义模板
- 分组
- 可搜索
- 远程搜索
- 创建条目
二、问题分析
- 如何处理父子组件的通信的呢?
- 下拉组件是怎么实现的?
数据属性和事件
这里就不单独把数据属性和事件拿出来讲了 用到什么就写什么吧,毕竟代码太多了 总共加起来好几千行!!!
复制代码
基础用法
基础用法所能用到的Dom
<el-input
v-model="selectedLabel"
:placeholder="currentPlaceholder"
:autocomplete="autoComplete || autocomplete"
:size="selectSize"
:disabled="selectDisabled"
:readonly="readonly"
:validate-event="false"
:class="{ 'is-focus': visible }"
:tabindex="multiple && filterable ? '-1' : null"
@focus="handleFocus"
@blur="handleBlur"
@keyup.native="debouncedOnInputChange"
@keydown.native.down.stop.prevent="navigateOptions('next')"
@keydown.native.up.stop.prevent="navigateOptions('prev')"
@keydown.native.enter.prevent="selectOption"
@keydown.native.esc.stop.prevent="visible = false"
@keydown.native.tab="visible = false"
@paste.native="debouncedOnInputChange"
@mouseenter.native="inputHovering = true"
@mouseleave.native="inputHovering = false"
>
<template slot="prefix" v-if="$slots.prefix">
<slot name="prefix"></slot>
</template>
<template slot="suffix">
<i v-show="!showClose" :class="['el-select__caret', 'el-input__icon', 'el-icon-' + iconClass]"></i>
<i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close" @click="handleClearClick"></i>
</template>
</el-input>
<transition name="el-zoom-in-top" @before-enter="handleMenuEnter" @after-leave="doDestroy">
<el-select-menu ref="popper" :append-to-body="popperAppendToBody" v-show="visible && emptyText !== false">
<el-scrollbar
tag="ul"
wrap-class="el-select-dropdown__wrap"
view-class="el-select-dropdown__list"
ref="scrollbar"
:class="{ 'is-empty': !allowCreate && query && filteredOptionsCount === 0 }"
v-show="options.length > 0 && !loading"
>
<el-option :value="query" created v-if="showNewOption"> </el-option>
<slot></slot>
</el-scrollbar>
<template v-if="emptyText && (!allowCreate || loading || (allowCreate && options.length === 0))">
<slot name="empty" v-if="$slots.empty"></slot>
<p class="el-select-dropdown__empty" v-else>
{{ emptyText }}
</p>
</template>
</el-select-menu>
</transition>
复制代码
基础用法中有哪些动作和效果!
- 首先呢 肯定是点击input框时的动作了 这里的话我没看到
click
事件,那么的话 element是用focus
事件代替了click
事件了
// 手动聚焦的方法
handleFocus(event) {
if (!this.softFocus) {
// 判断是否设置了(对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单)automatic-dropdown属性
// 或者是否可搜索(filterable)
if (this.automaticDropdown || this.filterable) {
// visible为true 显示聚焦的样式 并且展示下拉组件
this.visible = true;
if (this.filterable) {
// 菜单焦点为ture
this.menuVisibleOnFocus = true;
}
}
// 传递给用户对应的focus事件
this.$emit("focus", event);
} else {
this.softFocus = false;
}
},
复制代码
- 回头来看一下 下拉组件的Dom
<el-select-menu ref="popper" :append-to-body="popperAppendToBody" v-show="visible && emptyText !== false">
<el-scrollbar
tag="ul"
wrap-class="el-select-dropdown__wrap"
view-class="el-select-dropdown__list"
ref="scrollbar"
:class="{ 'is-empty': !allowCreate && query && filteredOptionsCount === 0 }"
v-show="options.length > 0 && !loading"
>
<el-option :value="query" created v-if="showNewOption"> </el-option>
<slot></slot>
</el-scrollbar>
<template v-if="emptyText && (!allowCreate || loading || (allowCreate && options.length === 0))">
<slot name="empty" v-if="$slots.empty"></slot>
<p class="el-select-dropdown__empty" v-else>
{{ emptyText }}
</p>
</template>
</el-select-menu>
复制代码
- 这里的
el-select-menu
组件代码比较简单
<div class="el-select-dropdown el-popper" :class="[{ 'is-multiple': $parent.multiple },
popperClass]" :style="{ minWidth: minWidth }">
<slot></slot>
</div>
复制代码
可以看到这是下拉框的整体的结构 然后剩下的所有内容都是通过slot来展示 然后被添加到 body 节点上了,通过 position
定位到了输入框的上方或者下方,并且可以根据输入框的位置进行调整。这里element通过很多的第三方库来实现了一部分功能。第三方库又是套用的关系,在el-select-menu中可以看见使用了 vue-popper
而在 vue-popper
中又引入了 popper-manager
,同时 vue-popper
又引入了第三方的定位库 popper.js
`vue-popper`:用于管理组件的弹出框,什么时候创建、在哪个位置创建、什么时候又需要销毁以及怎么销毁
`popup`:主要是做弹出框的打开和关闭操作
`popup-manager`:用来管理页面中所有的 modal 层
`popper.js`:第三方库,主要是用来定位弹出框的
复制代码
-
紧接着就是
el-scrollbar
这里的话就自己封装的滚动条组件了 -
最后最后就只剩下了
el-option
上代码
<li
@mouseenter="hoverItem"
@click.stop="selectOptionClick"
class="el-select-dropdown__item"
v-show="visible"
:class="{
selected: itemSelected,
'is-disabled': disabled || groupDisabled || limitReached,
hover: hover,
}"
>
<slot>
<span>{{ currentLabel }}</span>
</slot>
</li>
复制代码
这里发现下拉选择是一个个的li 这里ui的话是通过el-scrollbar
里面的render函数来进行创作出来的
这里的el-scrollbar
可以单独的拿出来讲