本文是 Element 的组件源码学习系列。
项目源码:ElemeFE/element | GitHub,Tag:v2.13.0
Row & Col 组件使用文档:Layout 布局
.vue 文件:/packages/col
.vue 文件:/packages/row
.scss 文件:/packages/theme-chalk/col.scss
.scss 文件:/packages/theme-chalk/row.scss
.d.ts 文件:/types/col.d.ts
.d.ts 文件:/types/row.d.ts
Props
首先我们来看一下 el-row
的 Props,了解使用这个组件的入参:
1 | { |
首先,看这个组件使用的是 render
方法,还是之前比较流行的写法,现在的模板大部分都使用 template
标签了。
tag
tag
是用来自定义元素标签的,默认情况下使用的是 <div>
元素。
el-row
和 el-col
本质上是 CSS flexbox
的属性封装,所以能使用 flexbox
的标签,都可以作为 tag
属性传入,来替换 <div>
元素。常见的场景,是 C 端 SEO,要提供语义化的 HTML 标签,这个使用就可以使用 tag
gutter
gutter
用来指定每一栏之间的间隔,默认是 0。
在 el-row
上设置了 gutter
属性后,会影响该 row
下面所有 el-col
元素,我们来看一下怎么实现的:
首先,我们来看一下 el-col
的相关代码:
1 | computed: { |
一个 el-row
组件的 gutter
属性,是通过它的父元素,或者祖先元素中最近的一个 el-row
组件决定的。首先,拿到最近的 el-row
组件,然后获得该组件的 gutter
配置。
在渲染的时候,由于 gutter
指的是两个 col
组件之间的总距离,所以映射到每一个 el-col
上,设置为 padding-left
和 padding-right
分为为二分之一。
注意!!如果第一个 el-col 的左侧也有二分之一 gutter 的长度,是不符合我们的预期的。因为第一个 el-col
应该是最左边没有边距的。
所以我们需要在 el-row
上面做文章,将第一个 el-col
的 paddingLeft
抵消掉。由于 padding
的值,不能设置为负数,参考 padding | MDN,所以我们只能将 el-row
的外边距设置为负数,来抵消第一个 el-col
的 paddingLeft
:
1 | computed: { |
(PS:
我本人对于 gutter
在 el-col
上的实现方案,有一些疑问。我觉得 gutter
的实现方案,在 el-col
上,应该使用 marginLeft
和 marginRight
。因为如果对 el-col
设置了背景颜色的话,使用 padding
的实现方法,间隔栏也会被染色,这个是不符合预期的。
当然这个问题也很好解决。需要我们对 el-col
的定位,是负责布局控制。而业务相关的设置,比如背景颜色,不应该直接设置在 el-col
上,而是在 el-col
中再包裹 div
元素,设置在 div
元素上,则间隔栏就不会被染色了。
span & offset & pull & push
span
属性是用来控制一个 el-col
栅格的宽度的。我们来看一下实现方案:
1 | ['span', 'offset', 'pull', 'push'].forEach(prop => { |
这里把 span & offset & pull & push
放在一起写,是因为这几个属性,背后的原理都是一样的,控制 HTML 元素的 CSS 属性。下面我们来一起看一下相关的 SCSS 实现:
1 | [class*="el-col-"] { |
我们来依次看下:
- 所有的
el-col
都设置了float: left
,来实现在一个el-row
中从左到右的顺序排列。并且设置了box-sizing: border-box
,这样方便对于宽度进行较为精确的控制,不会受到padding
和border
的宽度影响。 span
值的实现,当等于0
的时候,实现方案是display: none
。当不等于 0 的时候,通过width
设置的百分比。offset
是通过 margin 来实现的偏移量。
type=flex
通过在 el-row
上设置 type=flex
,可以使用 flexbox 的布局能力。我们来看一下具体的实现方案:
1 | render(h) { |
即,通过设置 type=flex
来开启 flexbox
布局,然后设置 justify
来配置 CSS justify-content
属性,以及 设置 align
来配置 align-items
属性。
1 | @include m(flex) { |
响应式布局
参照了 Bootstrap 的 响应式设计,预设了五个响应尺寸:xs
、sm
、md
、lg
和 xl
。
我们来看一下怎么实现的:
1 | ['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => { |
我们用 xs
属性来举例。
xs:<768px
响应式栅格数或者栅格属性对象
首先,当 el-col
设置了 xs
属性后,通过上面的 JS 代码,给 el-col
添加了相关的 CSS 类。我们来看一下这个类是怎么实现的呢。
1 | @include res(xs) { |
在这个 @include
里面,好像和 span
的设置没有什么不同,都是对于 el-col
的宽度进行修改。那 xs
的设置,如何触发祥响应式相关的设置呢?关键在 res(xs)
的 res
这个函数上。
在 col.scss
中,一开始引入了两个文件:
1 | @import "./common/var.scss"; |
res
函数的定义就可以在这两个文件中找到:
1 | // ./mixins/mixins.scss |
当调用 res(xs)
的时候,$key
就是 xs
,$map
使用的是默认值 $--breakpoints
,也就是 ./common/var.scss
中预先定义的响应式布局中常用的几种宽度。然后 res
函数会通过设置 @media only screen
的 CSS 属性,来实现响应式的能力。所以,el-col
的实现,本质上也是通过设置 @media
属性来实现的。