一、layout布局组件的引用方式
基础的引用方式如下
<el-row>
<el-col :span="24"><div class="grid-content bg-purple-dark"></div></el-col>
</el-row>
<el-row>
<el-col :span="12"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="12"><div class="grid-content bg-purple-light"></div></el-col>
</el-row>
二、功能点逐个击破
官网介绍的功能点如下
- ele的布局组件是使用基础的24分栏,迅速简便的创建布局。并通过 col 组件的 span 属性我们就可以自由地组合布局。
- Row 组件 提供 gutter 属性来指定每一栏之间的间隔,默认间隔为 0。
- 通过制定 col 组件的 offset 属性可以指定分栏偏移的栏数。
- 通过 flex 布局来对分栏进行灵活的对齐
- 参照了 Bootstrap 的 响应式设计,预设了五个响应尺寸:xs、sm、md、lg 和 xl,实现响应式布局。
我们可以根据功能点对组件进行分析
el-row的组件的源码:
export default {
name: 'ElRow', // 组件的name属性
componentName: 'ElRow',
// 接受props属性
props: {
// 通过设置tag,自定义渲染的元素标签
tag: {
type: String,
default: 'div'
},
// 栅格间隔
gutter: Number,
// 布局模式,可选择flex
type: String,
// flex 布局下的水平排列方式
justify: {
type: String,
default: 'start'
},
// flex 布局下的垂直排列方式
align: {
type: String,
default: 'top'
}
},
computed: {
// 如果设置了gutter,那么将其转化为margin对应值
// 比如设置了gutte=20,那么实际上是给当前元素设置了padding-left: 10px;padding-right: 10px;
style() {
const ret = {};
if (this.gutter) {
ret.marginLeft = `-${this.gutter / 2}px`;
ret.marginRight = ret.marginLeft;
}
return ret;
}
},
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);
}
};
el-col源码:
export default {
name: 'ElCol',
props: {
// 栅格占据的列数
span: {
type: Number,
default: 24
},
// 自定义元素标签
tag: {
type: String,
default: 'div'
},
// 栅格左侧的间隔格数
offset: Number,
// 栅格向左移动格数
pull: Number,
// 栅格像右移动格数
push: Number,
// <768px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
xs: [Number, Object],
// ≥768px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
sm: [Number, Object],
// ≥992px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
md: [Number, Object],
// ≥1200px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
lg: [Number, Object],
// ≥1920px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
xl: [Number, Object]
},
computed: {
gutter() {
// col逐级往上寻找父节点,判断是否是ElRow, 找到距离最近的父级ElRow
// 如果找到ElRow ,则返回gutter 属性,如果找不到继续往上直到不存在父节点则返回0
// 因为ElRow和ElCol可能嵌套了其他组件
let parent = this.$parent;
while (parent && parent.$options.componentName !== 'ElRow') {
parent = parent.$parent;
}
return parent ? parent.gutter : 0;
}
},
render(h) {
let classList = []; // 设置标签的class属性
let style = {}; // 设置标签的行内css
if (this.gutter) {
style.paddingLeft = this.gutter / 2 + 'px';
style.paddingRight = style.paddingLeft;
}
// 如果添加了span, offset, pull, push,那么将添加对应的class类
['span', 'offset', 'pull', 'push'].forEach(prop => {
if (this[prop] || this[prop] === 0) {
classList.push(
prop !== 'span'
? `el-col-${prop}-${this[prop]}`
: `el-col-${this[prop]}`
);
}
});
// 如果添加了xs, sm, md, lg, xl,那么将添加对应的class类
['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => {
if (typeof this[size] === 'number') {
classList.push(`el-col-${size}-${this[size]}`);
} else if (typeof this[size] === 'object') {
let props = this[size];
Object.keys(props).forEach(prop => {
classList.push(
prop !== 'span'
? `el-col-${size}-${prop}-${props[prop]}`
: `el-col-${size}-${props[prop]}`
);
});
}
});
return h(this.tag, {
class: ['el-col', classList],
style
}, this.$slots.default);
}
};
两个组件中都出现了render函数,先来看看render函数
render函数
大多数开发过程中,我们都会使用‘单文件组件’,在template中使用html语法组件页面,编译器拿到template模板时将其转义成vnode函数。
但是其实在一些场景中,也可以直接用JavaScript的完全编程能力,这就是渲染函数,即render函数。而用render函数构建DOM时,vue就免去了转译的过程。render函数就可以使用js语言来构建DOM
当使用render函数描述vnode时,vue提供一个函数,这个函数是构建dom所需要的工具,官网起名为createElement,简写成h函数。
render函数接受一个createElement参数,并返回该函数创建的vnode。
createElement接受三个参数
- 参数1:标签名,必填项。
- 参数2:该标签attribute 对应的数据对象。可选。
- 参数3:子虚拟节点
比如组件源码中的render函数
// 渲染函数
render(h) {
// 接受createElement函数,并返回该函数创建的vnode
// 该函数所需三个参数,标签名,属性,子虚拟节点
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);
}
下面我们整体梳理下这两个组件具体做了什么,一一解密每一个功能点是如何实现的。
解密布局
ele的布局组件是使用基础的24分栏,迅速简便的创建布局。并通过 col 组件的 span 属性我们就可以自由地组合布局。
<el-row>
<el-col :span="12"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="12"><div class="grid-content bg-purple-light"></div></el-col>
</el-row>
首先row和col一般情况下都是配合使用的,ele的布局组件是使用基础的24分栏,迅速简便的创建布局。通过 row 和 col 组件,并通过 col 组件的 span 属性我们就可以自由地组合布局。
默认div的宽度是100%独占一行的,让多个el-col在一行,让他们的宽占据一定的百分比,就可以实现分栏的效果。设置百分比,就用过设置不同的css即可实现
假设给span赋值12的时候,那么相当于给元素添加了一个el-col-12的class类,因为总共是24分栏,那么它的宽度就是50%。
.el-col-12 {
width: 50%
}
因此,通过设置span,可以给元素添加相应的class类名,从而设置对应的宽度,即可实现快速创建布局。
解密间隔
Row 组件 提供 gutter 属性来指定每一栏之间的间隔,默认间隔为 0。
<el-row :gutter="20">
<el-col :span="16"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="8"><div class="grid-content bg-purple"></div></el-col>
</el-row>
通过给row组件添加gutter值,就可以设置col的间隔,在源码中可以看到,col逐级往上寻找父节点,判断是否是row, 找到距离最近的父级row,如果找到row ,则返回父级的gutter 属性,如果找不到继续往上直到不存在父节点则返回0,因为row和col可能嵌套了其他组件。
获取到了gutter就通过给自身设置padding样式,结合box-sizing:border-box,就可以实现分栏间隔
比如给gutter设置20的时候,实际上是给元素添加padding-left:10px;padding-right:10px;
[class*=el-col-] {
float: left;
box-sizing: border-box;
}
{
padding-left: 10px;
padding-right: 10px;
}
解密偏移
通过制定 col 组件的 offset 属性可以指定分栏偏移的栏数。
<el-row :gutter="20">
<el-col :span="6"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="6" :offset="6"><div class="grid-content bg-purple"></div></el-col>
</el-row>
在源码中可以看到,给col设置offset属性,相当于给元素设置了el-col-offset-6的class类,这个类给元素设置了对应的margin百分比,即实现了对应的偏移数
.el-col-offset-6 {
margin-left: 25%;
}
同理,当给col设置pull=6时,实际上是给元素添加了el-col-pull-6类,设置了对应的定位偏移
.el-col-pull-6 {
position: relative;
right: 25%;
}
设置push时,是添加了el-col-push-6类
.el-col-push-6 {
position: relative;
left: 25%;
}
解密支持flex
通过 flex 布局来对分栏进行灵活的对齐
row可以通过type=flex将其设置为flex布局,然后用过justify设置flex布局下的水平排列方式,通过align设置布局下的垂直排列方式。
根据设置的justify的属性值,设置对应的class、比如justify="center",就设置了class为is-justify-center,从而设置了对应的样式
.el-row--flex.is-justify-center {
justify-content: center;
}
.el-row--flex {
display: flex;
}
algin也是同理。
解密响应式
参照了 Bootstrap 的 响应式设计,预设了五个响应尺寸:xs、sm、md、lg 和 xl
xs: <768px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
sm: ≥768px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
md: ≥992px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
lg: ≥1200px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
xl: ≥1920px 响应式栅格数或者栅格属性对象 eg: number/object (例如: {span: 4, offset: 4})
这个也很简单,比如下面这段响应式布局
<el-row :gutter="10">
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"><div class="grid-content bg-purple"></div></el-col>
</el-row>
编译出来,其实结合了media标签,设置对应的class
<div class="el-col el-col-24 el-col-xs-8 el-col-sm-6 el-col-md-4 el-col-lg-3 el-col-xl-1"></div>
最后编译出来的部分代码如下
@media only screen and (min-width: 768px){
....
}
总结:
其实ele的布局组件layout还是相对来说比较好理解的,就是通过设置对应的属性值,生成对应的class去渲染布局,然后用render函数再去渲染对应的vnode。