ElementUI 简要源码解析——Basic篇
layout 布局
row
布局组件中的父组件,用于控制子组件。很简单的一个布局标签,主要通过 justify 和 align 控制子元素的对齐方式,使用 render 函数通过传入的 tag 属性控制生成的标签。
在这里推荐学习下 render 函数和 jsx 的写法,因为之后比较复杂的组件都是通过 render函数 + jsx 的方式来写的。
// 核心代码 render(h) { return h(this.tag, { class: [ 'el-row', this.justify !== 'start' ? `is-justify-${this.justify}` : '', this.align !== 'top' ? `is-align-${this.align}` : '', { 'el-row--flex': this.type === 'flex' } ], style: this.style }, this.$slots.default); }
col
布局组件中的子组件,通过传入的props控制占据的列数、偏移、大小等,通过 foreach 对每个属性进行处理,生成包含对应样式的 classlist。
最后将 classlist 传入 createelement 函数(h)中的第二个参数(标签选项)中,如此,就生成了所需要的布局。
// 核心代码 render (h) { // 省略,通过props计算classlist return h(this.tag, { class: ['el-col', classlist], style }, this.$slots.default); }
container 布局容器
container
父容器组件,根据传入的direction字段,决定样式是水平还是垂直。当没有传入direction字段时,根据插槽中子组件是否含有 header 或 footer组件,如果含有则为垂直,否则为水平。
// 核心代码 computed: { isvertical() { if (this.direction === 'vertical') { return true; } else if (this.direction === 'horizontal') { return false; } return this.$slots && this.$slots.default ? this.$slots.default.some(vnode => { const tag = vnode.componentoptions && vnode.componentoptions.tag; return tag === 'el-header' || tag === 'el-footer'; }) : false; } }
header
最简单的组件之一,通过传入的 height 参数定义 style 高度。
aside
最简单的组件之一,通过传入的 width 参数控制 style 宽度。
main
真正意义上的最简单容器组件,包含插槽的纯容器。
el-footer
最简单的组件之一,通过传入的 height 参数定义 style 高度。
布局容器总结
何为容器?在我的理解中,容器就是一个限制大小的盒子。布局容器通过属性定义 header、aside、footer 的高宽的行内样式,接下来只需要定义 main 为 flex: 1 即可实现自适应布局。
icon 图标
感觉比较没有意义的一个组件,通过传入的 name 来组成类似于 el-icon-name
格式的类,然后将这个类定义在 i 标签内。不过大多数人都不会用这个组件,为什么?因为连官网推荐写法都是直接在 i 标签内添加对应图标的类。
button 按钮
button 组件是 basic 里面唯一一个稍微复杂一点的组件。它与其他 basic 组件的最大区别在于,通过 provide/inject 获取了祖先组件,然后参照祖先组件的尺寸参数,将 button 组件的尺寸参数设为一致。
button 组件的尺寸由三个因素决定:
- 直接设置组件的 size 属性,该因素具有最高优先级,类比于样式中的行内样式。
- 当 button 运用在 form 组件中,可以通过配置 form 组件的 size 属性来决定,当没有直接设置 button 组件的 size 属性时,由该因素决定 button 组件的尺寸,类比于样式中的样式继承。
- 当前两种因素都不存在时,由
this.$element
对象中的 size 属性决定。what?this.$element
什么鬼?它是接收初始化 elementui 时传入的尺寸参数的对象,包含两个属性:size 以及 zindex,方便全局定义各种组件的尺寸。类比于样式中的 body 样式继承。
// 核心代码 export default { name: 'elbutton', // 通过 inject 获取 elform 以及 elformitem 这两个组件 inject: { elform: { default: '' }, elformitem: { default: '' } }, // ... computed: { _elformitemsize() { return (this.elformitem || {}).elformitemsize; }, buttonsize() { // 三种因素决定按钮的尺寸 return this.size || this._elformitemsize || (this.$element || {}).size; }, //... }, // ... };
link 文字链接
和一般文字链接区别不大的一个组件,主要区别在于两点:
- 可以通过 disabled 属性设置禁用,原理是当 disabled 属性为 false 时,将 a 标签的 href 属性置为 null,同时阻止阻止其向上抛出 click 事件。
- 灵活运用插槽,看似该组件只有一个插槽,实则拥有两个插槽,一个是匿名插槽,一个是名为 icon 的具名插槽,通过合理的设置可以快速实现各种需求。
值得一提的是,该组件还使用了一个通用组件开发的小技巧:通过 v-bind="$attrs"
进行快速属性赋值。为什么要这样做?因为 a 标签可以含有各种 html 属性,而这些属性我们不可能一一通过 props 接收然后赋值到 a 标签的属性上。
因此,我们可以通过 v-bind="$attrs"
无视传入的属性是什么,一股脑将其赋值到 a 标签上。这相当于,开发者可以直接像操作 a 标签一样操作 link 组件,大大方便了组件的使用。
<template> <a :class="[ 'el-link', type ? `el-link--${type}` : '', disabled && 'is-disabled', underline && !disabled && 'is-underline' ]" :href="disabled ? null : href" v-bind="$attrs" @click="handleclick" > <i :class="icon" v-if="icon"></i> <span v-if="$slots.default" class="el-link--inner"> <slot></slot> </span> <template v-if="$slots.icon"><slot v-if="$slots.icon" name="icon"></slot></template> </a> </template>
总结
通读了 basic 系列的源码,可以发现,这一部分的源码其实相对比较简单,但是,却又很多的小细节点值得学习,比如:通过 v-bind=$attrs
定义标签属性、通过组合插槽方便组件使用、通过多种因素设定属性以及各种缺省设计等等。
上一篇: Python 等分切分数据及规则命名
下一篇: vmware上安装centos7虚拟机