admin管理员组

文章数量:1026989

Element源码系列——Row以及Col组件

Element源码系列——Row以及Col组件

      • Element源码系列——Row以及Col组件
        • 序言
        • Row
        • Col
        • 总结

序言

官网中对Layout 布局的定义是: 通过基础的 24 分栏,迅速简便地创建布局.

Element中的栅格系统也与Bootstrap类似,组件开发的目的都是为了解决基本布局定位的问题.

Layout主要组件为Row与Col,其中Row为行布局容器,Col为列布局容器.

话不多说,先来看看Row的部分.

Row

Row是行布局容器,从功能上来说它的作用是控制内部元素的排列方式.我们边看代码边说.

name: 'ElRow',componentName: 'ElRow',// Row组件中的props
props: {tag: {type: String,    // 通过传入不同的tag来让row生成不同的标签default: 'div'},gutter: Number,    // gutter翻译是排水沟,非常贴切,它的作用是控制Row中元素的间隔type: String,      // type主要是控制是否以flex进行布局justify: {         // flex元素中水平排列规则type: String,default: 'start'},align: {type: String,    // flex元素中垂直排列规则default: 'top'}
}

将props与CSS3中的flex布局相结合,Row的用法就非常清晰了

参数说明类型可选值默认值
gutter栅格间隔number0
type布局模式,可选 flex,现代浏览器下有效string
justifyflex 布局下的水平排列方式stringstart/end/center/space-around/space-betweenstart
alignflex 布局下的垂直排列方式stringtop/middle/bottomtop
tag自定义元素标签string*div

Row的代码很精简,但是有两个细节咱们需要处理.

1.通过gutter设置元素间隔时,由于设置子元素的padding-left与padding-right来控制间隔,导致首尾也会有间隔.

通过控制Row的margin-left和margin-right为负值可以解决.

computed: {style() {const ret = {};if (this.gutter) {ret.marginLeft = `-${this.gutter / 2}px`;ret.marginRight = ret.marginLeft;}return ret;}
}

2.因为需要自定义渲染HTML标签,所以常规的template写法不可用了,需要使用render函数进行渲染.

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' } // flex布局样式],style: this.style}, this.$slots.default);
}

到此Row的代码就结束了,一个组件的使用体验完全取决于程序猿的细节把控,千万不要忽视细节!

Col

Col要比Row稍微复杂一些,我们先从简单的一步一步来看.

同样的,先看下props中有哪些属性:

name: 'ElCol',component: 'ElCol',props: {span: {                 // 控制col在父元素中占的比例type: Number,default: 24},tag: {type: String,       // 自定义渲染的标签default: 'div'},offset: Number,         // 栅格左侧的间隔格数pull: Number,           // 栅格向右移动格数push: Number,           // 栅格向左移动格数xs: [Number, Object],   // <768px 响应式栅格数或者栅格属性对象sm: [Number, Object],   // ≥768px 响应式栅格数或者栅格属性对象md: [Number, Object],   // ≥992px 响应式栅格数或者栅格属性对象lg: [Number, Object],   // ≥1200px 响应式栅格数或者栅格属性对象xl: [Number, Object]    // ≥1920px 响应式栅格数或者栅格属性对象
}

从props可以看出主要分为元素基本大小与定位控制,响应式控制功能点,而响应式控制是基于第一个功能点为基础.

所以我们先看看span、offset、pull、push是如何被使用的.

// col.js文件
render(h) {let classList = [];// 这里逻辑是通过比对props对象,生成对应的CSS规则['span', 'offset', 'pull', 'push'].forEach(prop => {if (this[prop] || this[prop] === 0) {classList.push(prop !== 'span'? `el-col-${prop}-${this[prop]}` // e.g el-col-offset-6: `el-col-${this[prop]}`         // e.g el-col-5);}});....
}
// 具体对应样式文件col.scss
// scss可以通过@for实现循环添加样式
// 栅格化具体计算公式 (1 / 24 * $i * 100) * 1%
@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%;}
}

同样的,响应式的原理相差不多,差别在于响应式支持传入一个对象.

let classList = [];['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => {// 这里分为传入对象以及数字两种情况if (typeof this[size] === 'number') {classList.push(`el-col-${size}-${this[size]}`); // e.g el-col-xs-4} 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]}` // e.g el-col-xs-offset-4: `el-col-${size}-${props[prop]}`         // e.g el-col-xs-4);});}
});

如何优雅的使用scss写出响应式:

// var.scss// 定义断点值的变量
$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;// 定义断点对象
$--breakpoints: ('xs' : (max-width: $--sm),'sm' : (min-width: $--sm),'md' : (min-width: $--md),'lg' : (min-width: $--lg),'xl' : (min-width: $--xl)
);// mixins.scss
@mixin res($key, $map: $--breakpoints) {// 循环断点Map,如果存在则返回@if map-has-key($map, $key) {@media only screen and #{inspect(map-get($map, $key))} {@content;}} @else {@warn "Undefined points: `#{$map}`";}
}// col.scss
@include res(xs) {.el-col-xs-0 {display: none;}@for $i from 0 through 24 {.el-col-xs-#{$i} {width: (1 / 24 * $i * 100) * 1%;}.el-col-xs-offset-#{$i} {margin-left: (1 / 24 * $i * 100) * 1%;}.el-col-xs-pull-#{$i} {position: relative;right: (1 / 24 * $i * 100) * 1%;}.el-col-xs-push-#{$i} {position: relative;left: (1 / 24 * $i * 100) * 1%;}}
}

最后,有个小细节处理下.

Row中的gutter是负责每个元素的间隔,它是统一的数值.并且需要传入到Col中使用,用来生成子元素的pading-left与padding-right.如果让用户传入的话本身不太合理,并且也增加组件使用的复杂性.

// 反向递归查找第一个名为ElRow的父组件
computed: {gutter() {let parent = this.$parent;while (parent && parent.$optionsponentName !== 'ElRow') {parent = parent.$parent;}return parent ? parent.gutter : 0;}
},
render(h) {let style = {};// 通过父组件的gutter生成自生的padding-left与padding-rightif (this.gutter) {style.paddingLeft = this.gutter / 2 + 'px';style.paddingRight = style.paddingLeft;}return h(this.tag, { // render返回渲染class: ['el-col', classList],style}, this.$slots.default);
}

总结

Row和Col就结束了,组件还是比较简单的,但是其中的细节处理与思想才是重点,希望可以帮到你!

感谢您的阅读!

再次感谢element团队的贡献!

Element源码系列——Row以及Col组件

Element源码系列——Row以及Col组件

      • Element源码系列——Row以及Col组件
        • 序言
        • Row
        • Col
        • 总结

序言

官网中对Layout 布局的定义是: 通过基础的 24 分栏,迅速简便地创建布局.

Element中的栅格系统也与Bootstrap类似,组件开发的目的都是为了解决基本布局定位的问题.

Layout主要组件为Row与Col,其中Row为行布局容器,Col为列布局容器.

话不多说,先来看看Row的部分.

Row

Row是行布局容器,从功能上来说它的作用是控制内部元素的排列方式.我们边看代码边说.

name: 'ElRow',componentName: 'ElRow',// Row组件中的props
props: {tag: {type: String,    // 通过传入不同的tag来让row生成不同的标签default: 'div'},gutter: Number,    // gutter翻译是排水沟,非常贴切,它的作用是控制Row中元素的间隔type: String,      // type主要是控制是否以flex进行布局justify: {         // flex元素中水平排列规则type: String,default: 'start'},align: {type: String,    // flex元素中垂直排列规则default: 'top'}
}

将props与CSS3中的flex布局相结合,Row的用法就非常清晰了

参数说明类型可选值默认值
gutter栅格间隔number0
type布局模式,可选 flex,现代浏览器下有效string
justifyflex 布局下的水平排列方式stringstart/end/center/space-around/space-betweenstart
alignflex 布局下的垂直排列方式stringtop/middle/bottomtop
tag自定义元素标签string*div

Row的代码很精简,但是有两个细节咱们需要处理.

1.通过gutter设置元素间隔时,由于设置子元素的padding-left与padding-right来控制间隔,导致首尾也会有间隔.

通过控制Row的margin-left和margin-right为负值可以解决.

computed: {style() {const ret = {};if (this.gutter) {ret.marginLeft = `-${this.gutter / 2}px`;ret.marginRight = ret.marginLeft;}return ret;}
}

2.因为需要自定义渲染HTML标签,所以常规的template写法不可用了,需要使用render函数进行渲染.

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' } // flex布局样式],style: this.style}, this.$slots.default);
}

到此Row的代码就结束了,一个组件的使用体验完全取决于程序猿的细节把控,千万不要忽视细节!

Col

Col要比Row稍微复杂一些,我们先从简单的一步一步来看.

同样的,先看下props中有哪些属性:

name: 'ElCol',component: 'ElCol',props: {span: {                 // 控制col在父元素中占的比例type: Number,default: 24},tag: {type: String,       // 自定义渲染的标签default: 'div'},offset: Number,         // 栅格左侧的间隔格数pull: Number,           // 栅格向右移动格数push: Number,           // 栅格向左移动格数xs: [Number, Object],   // <768px 响应式栅格数或者栅格属性对象sm: [Number, Object],   // ≥768px 响应式栅格数或者栅格属性对象md: [Number, Object],   // ≥992px 响应式栅格数或者栅格属性对象lg: [Number, Object],   // ≥1200px 响应式栅格数或者栅格属性对象xl: [Number, Object]    // ≥1920px 响应式栅格数或者栅格属性对象
}

从props可以看出主要分为元素基本大小与定位控制,响应式控制功能点,而响应式控制是基于第一个功能点为基础.

所以我们先看看span、offset、pull、push是如何被使用的.

// col.js文件
render(h) {let classList = [];// 这里逻辑是通过比对props对象,生成对应的CSS规则['span', 'offset', 'pull', 'push'].forEach(prop => {if (this[prop] || this[prop] === 0) {classList.push(prop !== 'span'? `el-col-${prop}-${this[prop]}` // e.g el-col-offset-6: `el-col-${this[prop]}`         // e.g el-col-5);}});....
}
// 具体对应样式文件col.scss
// scss可以通过@for实现循环添加样式
// 栅格化具体计算公式 (1 / 24 * $i * 100) * 1%
@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%;}
}

同样的,响应式的原理相差不多,差别在于响应式支持传入一个对象.

let classList = [];['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => {// 这里分为传入对象以及数字两种情况if (typeof this[size] === 'number') {classList.push(`el-col-${size}-${this[size]}`); // e.g el-col-xs-4} 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]}` // e.g el-col-xs-offset-4: `el-col-${size}-${props[prop]}`         // e.g el-col-xs-4);});}
});

如何优雅的使用scss写出响应式:

// var.scss// 定义断点值的变量
$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;// 定义断点对象
$--breakpoints: ('xs' : (max-width: $--sm),'sm' : (min-width: $--sm),'md' : (min-width: $--md),'lg' : (min-width: $--lg),'xl' : (min-width: $--xl)
);// mixins.scss
@mixin res($key, $map: $--breakpoints) {// 循环断点Map,如果存在则返回@if map-has-key($map, $key) {@media only screen and #{inspect(map-get($map, $key))} {@content;}} @else {@warn "Undefined points: `#{$map}`";}
}// col.scss
@include res(xs) {.el-col-xs-0 {display: none;}@for $i from 0 through 24 {.el-col-xs-#{$i} {width: (1 / 24 * $i * 100) * 1%;}.el-col-xs-offset-#{$i} {margin-left: (1 / 24 * $i * 100) * 1%;}.el-col-xs-pull-#{$i} {position: relative;right: (1 / 24 * $i * 100) * 1%;}.el-col-xs-push-#{$i} {position: relative;left: (1 / 24 * $i * 100) * 1%;}}
}

最后,有个小细节处理下.

Row中的gutter是负责每个元素的间隔,它是统一的数值.并且需要传入到Col中使用,用来生成子元素的pading-left与padding-right.如果让用户传入的话本身不太合理,并且也增加组件使用的复杂性.

// 反向递归查找第一个名为ElRow的父组件
computed: {gutter() {let parent = this.$parent;while (parent && parent.$optionsponentName !== 'ElRow') {parent = parent.$parent;}return parent ? parent.gutter : 0;}
},
render(h) {let style = {};// 通过父组件的gutter生成自生的padding-left与padding-rightif (this.gutter) {style.paddingLeft = this.gutter / 2 + 'px';style.paddingRight = style.paddingLeft;}return h(this.tag, { // render返回渲染class: ['el-col', classList],style}, this.$slots.default);
}

总结

Row和Col就结束了,组件还是比较简单的,但是其中的细节处理与思想才是重点,希望可以帮到你!

感谢您的阅读!

再次感谢element团队的贡献!

本文标签: Element源码系列Row以及Col组件