element-ui button layout container input
在项目里经常使用element-ui,有点好奇一个工业级的组件库怎么实现的。
首先
从github clone element-ui的源码
1 | git clone https://github.com/ElemeFE/element.git |
element-ui的源码结构如下:
1 | CHANGELOG.en-US.md LICENSE element_logo.svg src |
组件的源码在packages目录下,使用make dev
可以运行examples目录下的样例代码,通过http://127.0.0.1:8085/
来访问。在examples引用了packages里面的组件,这样修改里面的组件立马可以看到效果。scss文件在packages/theme-chalk
中。
Element-UI BEM命名
在element-ui中css的命名方式使用BEM(block、element、modified)方式。将所有东西划分为独立的模块,block承载了element,modified表达状态。在BEM中,使用双下划线连接Block和Element,比如menu__item。使用单下划线连接Block和Modified或者连接Element和Modified,比如menu_active。当这个组件需要多个单词来描述时,就用-中划线来连接多个单词,比如start-menu。
在theme-chalk/src/mixins/mixin.scss
中定义了针对BEM格式的mixin。
对于block,传入名称后被替换成
el-名称
。1
2
3
4
5
6
7@mixin b($block) {
$B: $namespace+'-'+$block !global;
.#{$B} {
@content;
}
}对于element,将类名改为
.el-row__unit
,@content
表示之后@include
里面包括的属性。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24@mixin e($element) {
$E: $element !global;
$selector: &;
$currentSelector: "";
@each $unit in $element {
$currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","};
}
@if hitAllSpecialNestRule($selector) {
@at-root {
#{$selector} {
#{$currentSelector} {
@content;
}
}
}
} @else {
@at-root {
#{$currentSelector} {
@content;
}
}
}
}对于modifer,类名改为
el-row—-flex,
1
2
3
4
5
6
7
8
9
10
11
12
13@mixin m($modifier) {
$selector: &;
$currentSelector: "";
@each $unit in $modifier {
$currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
}
@at-root {
#{$currentSelector} {
@content;
}
}
}
Layout布局组件
layout是由ElRow、ElCol组成。
El-Row
El-Row有5个属性,默认是一个div块,通过tag来改变由什么实现。
gutter:设置列之间的间隔。配置后,El-Col会读取父节点的gutter属性,也就是El-Col的gutter来设置与其他列之间padding。
type:设置display为flex布局。这样就可以设置Row中的元素按照什么形式进行对齐。
El-Col
El-Col配合El-Row实现栅栏布局,关键在于pacages/theme-chalk/src/col.scss
中,将每一行分割为24等份。
默认使用float:left
。当Row使用flex后变为弹性项目。
1 | @for $i from 0 through 24 { |
Container布局容器组件
布局容器组件包括:container、header、aside、main、footer。
Container:使用flex布局,通过一个计算属性
isVertical
来修改子元素方向。内部使用<section></section>
Header:内部使用
<header></header>
实现。表示section的页眉。Aside:内部使用
<aside></aside>
实现,表示侧边栏,存放相关资料、标签。Main:内部使用
<main></main>
实现Footer:内部使用
<footer></footer>
实现,表示section的页脚。
之所以这么实现,为了web语意化,也就是让正确的标签做正确的事情。便于浏览器搜索引擎解析,利于爬虫标记、利于SEO。
Icon图标组件
直接通过el-icon-iconName
来使用。
组件代码在/packages/icon/src/
中,其中icon是<i :class="'el-icon-'+name"></i>
。关键在icon的scss文件。
这里使用的一种技术叫webfont,比如下面的编辑图标:
1 | .el-icon-edit:before { |
在unicode中E000到F8FF属于用户造字区,elementui首先在icon.scss中通过@font-face
下载elementui自己的字体,这个字体里有自定义的矢量图标。
1 | @font-face { |
然后所有的el-icon都会使用这个叫element-icons
的字体。
1 | [class^="el-icon-"], [class*=" el-icon-"] { |
这个字体文件中编号E78C
是一个edit的图标。
Button按钮组件
El-button内部使用的是<button></button>
。包含属性:
- type:通过type组合成
class: el-button—primary
来切换样式。 - size、loading、disabled、plain、autofocus、round、circle控制相关样式。
里面少见的是vue对象的inject属性:elForm、elFormItem。
elForm是由ElForm这个祖先组件提供,向所有后代提供表格这个组件的访问。而elFormItem是由ElFormItem提供。之所以提供这两个依赖是因为button常常用于表格中,所以需要统一大小。
- 可以看到对button的原生click事件捕获同时emit click event给el-button的父节点。这样在外部使用el-button时就不需要专门写@click.native修饰符,之所以这样因为v-on默认认为的click事件是自定义事件。
1 | <template> |
Input组件
Input组件分为两种:一种是普通input、另一种是textarea。
Element UI Mixins
Element-ui通过mixins给input 组件添加了broadcast、dispatch
两个函数,broadcast用于祖先组件向指定的子组件emit事件,dispatch用于子组件向祖先组件emit事件。
普通input组件
首先,从结构来说,input前后分别可以添加prepend、append,用于比如按钮和标签这样的需求。对于input中的内容,包含前置内容、后置内容.
1 | <template> |
input会监听的事件有:
input事件:每当输入的时候都会触发。
change事件:当input失去焦点且内容改变时触发。
blur事件:当input失去焦点时触发。
composition事件:复合事件,当用手机输入法时,会显示选择框,选择框打开也就是compositionstart事件,选择好就是compositionend事件,因为在输入时也会触发input事件,所以应该等选择好输入的词后在进行handleInput()。
1
2
3
4
5
6
7
8handleInput(event) {
if (this.isOnComposition) return; // 当composition事件完成时,再进行之后的逻辑
this.$emit('input', event.target.value);
this.$nextTick(() => {
let input = this.getInput();
input.value = this.value;
});
},
值验证:
与button一样,当input放在form中时,被注入ElForm和ElFormItem,并有一个watcher监听value的变化,每当发生变化便会dispatch到父节点ElForm,同时验证value合法。
input组件属性:
对于没有写入props的属性,input使用v-bind="$attrs"
来绑定
Textarea组件
textarea最复杂的功能是根据内容增多使得组件的高度变大。通过watcher监听value的变化:
1 | watch: { |
每次变化都会调用this.resizeTextarea
,然后计算textarea在当前行高下的整体高度
1 | export default function calcTextareaHeight( |
计算过程:首先创建隐藏textarea,将内容放入其中,看看scrollHeight是多少,将textarea的高度设置为这个值。如果指定minRows、maxRows时,textarea的高度就是scrollHeight的高度
总结
感觉写一个组件库需要考虑好多细节,这个就很难,得有很多经验才行。
- 本文链接:https://dowob.cn/2019/06/05/element-ui-button-border-layout-container-link/
- 版权声明:本站所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!