本文是 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 属性来实现的。