Layout 布局通过基础的 24 分栏,迅速简便地创建布局。只需引用row和col组件,就能快速的创建布局,基于24等分的原理,可以设置各个col所占的等分,使布局变得更简单。Layout 还参照了 Bootstrap 的 响应式设计,预设了五个响应尺寸:xs、sm、md、lg 和 xl。通过源码阅读,来了解下element-ui源码中是怎么实现分栏布局和响应式布局的。
<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>
1. row组件
el-row组件的代码相对比较简单,使用的是函数式组件,根据传入的标签来创建一个容器,默认为div容器,然后再根据传入的布局类型,排列方式,栅格间隔等参数来设置对应的CSS。代码如下所示:
export default {
name: 'ElRow',
componentName: 'ElRow',
props: {
tag: {
type: String,
default: 'div'
},
gutter: Number,
type: String,
justify: {
type: String,
default: 'start'
},
align: {
type: String,
default: 'top'
}
},
computed: {
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);
}
};
2.col组件
2.1组件介绍
el-row可以理解成一个容器,在element-ui中将窗口划分24等分,每一等分可以用一个el-col来添加内容,也可以为每一个el-col设置占多少等份,或者偏移多少份。其js的源码相对比较简单,就是简单的创建元素,为元素设置对应的样式。其核心主要是CSS代码。
export default {
name: 'ElCol',
props: {
span: {
type: Number,
default: 24
},
tag: {
type: String,
default: 'div'
},
offset: Number,
pull: Number,
push: Number,
xs: [Number, Object],
sm: [Number, Object],
md: [Number, Object],
lg: [Number, Object],
xl: [Number, Object]
},
computed: {
/**
* 获取父组件'el-row'的栅格间格
*/
gutter() {
let parent = this.$parent;
while (parent && parent.$options.componentName !== 'ElRow') {
parent = parent.$parent;
}
return parent ? parent.gutter : 0;
}
},
render(h) {
let classList = [];
let style = {};
// 设置栅格间隔的样式
if (this.gutter) {
style.paddingLeft = this.gutter / 2 + 'px';
style.paddingRight = style.paddingLeft;
}
//根据传递的,span,offset,pull,push等属性值,生成相应的样式名称
['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'].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);
}
};
2.2 样式介绍
el-col组件的重点主要是样式相关的,实现分栏和响应式的核心都是由样式来决定的,下面主要讲下怎么进行分栏和实现响应式。
2.1 24等份
el-col的核心是将el-row分成 24 等份来迅速简便地创建布局。其平分原理主要是使用百分比来做为元素的宽度,使用sass中的for来循环的生成24等分,如下所示:
@for $i from 0 through 24 {
.el-col-#{$i} {
width: (1 / 24 * $i * 100) * 1%;
}
.el-col-offset-#{$i} {
margin-left: (1 / 24 * $i * 100) * 1%;
}
.el-col-pull-#{$i} {
position: relative;
right: (1 / 24 * $i * 100) * 1%;
}
.el-col-push-#{$i} {
position: relative;
left: (1 / 24 * $i * 100) * 1%;
}
}
for:该指令可以在限制的范围内重复输出格式,每次按要求(变量的值)对输出结果做出变动。这个指令包含两种格式:
@for $var from <start> through <end>,或者@for $var from <start> to <end>,区别在于through与to的含义:当使用through时,条件范围包含<start>与<end>的值,而使用to时条件范围只包含<start>的值不包含<end>的值。
2.2 响应式布局
响应式布局主要是使用media,在不同屏幕下设置对应的样式,el-col主要设置了768px、992px、1200px、1920px等4种宽度的屏幕:
$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;
$--breakpoints: (
'xs' : (max-width: $--sm - 1),
'sm' : (min-width: $--sm),
'md' : (min-width: $--md),
'lg' : (min-width: $--lg),
'xl' : (min-width: $--xl)
);
$--breakpoints-spec: (
'xs-only' : (max-width: $--sm - 1),
'sm-and-up' : (min-width: $--sm),
'sm-only': "(min-width: #{$--sm}) and (max-width: #{$--md - 1})",
'sm-and-down': (max-width: $--md - 1),
'md-and-up' : (min-width: $--md),
'md-only': "(min-width: #{$--md}) and (max-width: #{$--lg - 1})",
'md-and-down': (max-width: $--lg - 1),
'lg-and-up' : (min-width: $--lg),
'lg-only': "(min-width: #{$--lg}) and (max-width: #{$--xl - 1})",
'lg-and-down': (max-width: $--xl - 1),
'xl-only' : (min-width: $--xl),
);
设置好对应的屏幕尺寸后,再根据传入的参数来生成对应的css样式代码,源码中使用的是一个res的混合器来生成的,如下所示:
@mixin res($key, $map: $--breakpoints) {
// 循环断点Map,如果存在则返回
@if map-has-key($map, $key) {
@media only screen and #{inspect(map-get($map, $key))} {
@content;
}
} @else {
@warn "Undefeined points: `#{$map}`";
}
}
- map-has-key:用于判断字典中是否包含了对应的
key。- inspect:返回一个字符串的表示形式,
value是一个sass表达式。