Layout
布局通过基础的 24 分栏,迅速简便地创建布局。只需引用row
和col
组件,就能快速的创建布局,基于24
等分的原理,可以设置各个col
所占的等分,使布局变得更简单。Layout
还参照了 Bootstrap
的 响应式设计,预设了五个响应尺寸:xs
、sm
、md
、lg
和 xl
。通过源码阅读,来了解下element-ui
源码中是怎么实现分栏布局和响应式布局的。
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
,或者through @for $var from
,区别在于to through
与to
的含义:当使用through
时,条件范围包含与
的值,而使用
to
时条件范围只包含的值不包含
的值。
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
表达式。