Element-UI源码——Layout布局

154 阅读2分钟

Layout布局

el-row

文件位置

  • Element-ui>packages>row>src>row.js
  • Element-ui>packages>theme-chalk>src>row.scss

Js代码

 export default {
   //组件的名字
   name: 'ElRow',
   //element-ui自定义的属性可以通过this.$options.componentName获得
   componentName: 'ElRow',
 ​
   props: {
     tag: {
       //自定义元素标签 默认为div标签 可以修改
       type: String,
       default: 'div'
     },
     //栅格间隔 px
     gutter: Number,
     //布局模式
     type: String,
     //flex 布局下的水平排列方式
     justify: {
       type: String,
       default: 'start'
     },
     //flex 布局下的垂直排列方式
     align: String
   },
 ​
   computed: {
     style() {
       const ret = {};
       if (this.gutter) {
         //左右设置一个-gutter/2的值 因为gutter在左右加上了padding-left和padding-right
        // 这样会导致两边没有对齐父元素,所以加上负margin,对齐两侧
         ret.marginLeft = `-${this.gutter / 2}px`;
         ret.marginRight = ret.marginLeft;
       }
       return ret;
     }
   },
 //通过渲染函数的形式编写
   render(h) {
     //第一个参数:标签
     //第二个参数:配置 与模板中属性对应的数据对象
     //第三个参数:子虚拟节点 插槽中的内容 写在el-row标签内部的部分
     return h(this.tag, {
       //class el-row is-justify-xx is-align-xx el-row--flex
       class: [
         'el-row',
         this.justify !== 'start' ? `is-justify-${this.justify}` : '',
         this.align ? `is-align-${this.align}` : '',
         { 'el-row--flex': this.type === 'flex' }
       ],
       //上面的计算属性
       style: this.style
     }, this.$slots.default);
   }
 };
 ​

Row Attributes

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

样式文件

 @import "common/var";
 @import "mixins/mixins";
 @import "mixins/utils";
 @include b(row) {
   position: relative;
   box-sizing: border-box;
   @include utils-clearfix;
 ​
   @include m(flex) {
     display: flex;
     &:before,
     &:after {
       display: none;
     }
 ​
     @include when(justify-center) {
       justify-content: center;
     }
     @include when(justify-end) {
       justify-content: flex-end;
     }
     @include when(justify-space-between) {
       justify-content: space-between;
     }
     @include when(justify-space-around) {
       justify-content: space-around;
     }
 ​
     @include when(align-top) {
       align-items: flex-start;
     }
 ​
     @include when(align-middle) {
       align-items: center;
     }
     @include when(align-bottom) {
       align-items: flex-end;
     }
   }
 ​
 }
 ​
 ​

@include b 混入了mixins/mixins中的b其中在config.scss定义了 BEM的Class命名风格

 $namespace: 'el';  组件名 el-row 
 $element-separator: '__';  元素子元素 加上__ el-input__inner
 $modifier-separator: '--';  修饰符 el-button-primary
 $state-prefix: 'is-'; 状态的前缀 is-disabled
 mixins/mixins文件
 @mixin b($block) {
   定义变量
   $B: $namespace+'-'+$block !global;
   使用变量
   .#{$B} {
     @content;
   }
 }

@include utils-clearfix 混入utils.scss 中的utils-clearfix 清除浮动

 @mixin utils-clearfix {
   $selector: &;
 ​
   @at-root {
     #{$selector}::before,
     #{$selector}::after {
       display: table;
       content: "";
     }
     #{$selector}::after {
       clear: both
     }
   }
 }

@include m 混入mixins中的m

 @mixin m($modifier) {
   $selector: &;
   $currentSelector: "";
   遍历修饰符 生成class名并拼接 el-row--flex
   @each $unit in $modifier {
     $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
   }
 ​
   @at-root {
     #{$currentSelector} {
       @content;
     }
   }
 }

@include when的混入,其实就是用来生成is-开头表示状态用的class

 @mixin when($state) {
   @at-root {
     &.#{$state-prefix + $state} {
       @content;
     }
   }
 }

el-col

文件位置

  • Element-ui>packages>row>src>col.js
  • Element-ui>packages>theme-chalk>src>col.scss

js代码

 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: {
     gutter() {
       //获取父对象
       let parent = this.$parent;
       //一直向上寻找父对象 直到找到父对象名字为ElRow也就是el-row
       while (parent && parent.$options.componentName !== 'ElRow') {
         parent = parent.$parent;
       }
       //获取父对象上的gutter 栅格间隔
       return parent ? parent.gutter : 0;
     }
   },
   render(h) {
     let classList = [];
     let style = {};
     //给左右两侧加上padding
     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);
   }
 };
 ​

Col Attributes

参数说明类型可选值默认值
span栅格占据的列数number24
offset栅格左侧的间隔格数number0
push栅格向右移动格数number0
pull栅格向左移动格数number0
xs<768px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
sm≥768px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
md≥992px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
lg≥1200px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
xl≥1920px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
tag自定义元素标签string*div

样式文件

 @import "./common/var.scss";
 @import "./mixins/mixins.scss";
 每一个el-col开头的元素设置了左浮动和border-box属性
 [class*="el-col-"] {
   float: left;
   box-sizing: border-box;
 }
 ​
 .el-col-0 {
   display: none;
 }
 循环从0-24设置spanoffsetpullpush的样式
 @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%;
   }
 }
 ​
 @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%;
     }
   }
 }
 ​
 @include res(sm) {
   .el-col-sm-0 {
     display: none;
   }
   @for $i from 0 through 24 {
     .el-col-sm-#{$i} {
       width: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-sm-offset-#{$i} {
       margin-left: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-sm-pull-#{$i} {
       position: relative;
       right: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-sm-push-#{$i} {
       position: relative;
       left: (1 / 24 * $i * 100) * 1%;
     }
   }
 }
 ​
 @include res(md) {
   .el-col-md-0 {
     display: none;
   }
   @for $i from 0 through 24 {
     .el-col-md-#{$i} {
       width: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-md-offset-#{$i} {
       margin-left: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-md-pull-#{$i} {
       position: relative;
       right: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-md-push-#{$i} {
       position: relative;
       left: (1 / 24 * $i * 100) * 1%;
     }
   }
 }
 ​
 @include res(lg) {
   .el-col-lg-0 {
     display: none;
   }
   @for $i from 0 through 24 {
     .el-col-lg-#{$i} {
       width: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-lg-offset-#{$i} {
       margin-left: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-lg-pull-#{$i} {
       position: relative;
       right: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-lg-push-#{$i} {
       position: relative;
       left: (1 / 24 * $i * 100) * 1%;
     }
   }
 }
 ​
 @include res(xl) {
   .el-col-xl-0 {
     display: none;
   }
   @for $i from 0 through 24 {
     .el-col-xl-#{$i} {
       width: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-xl-offset-#{$i} {
       margin-left: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-xl-pull-#{$i} {
       position: relative;
       right: (1 / 24 * $i * 100) * 1%;
     }
 ​
     .el-col-xl-push-#{$i} {
       position: relative;
       left: (1 / 24 * $i * 100) * 1%;
     }
   }
 }

res的混入接收两个参数,$key表示传入响应式的key值(xs、sm等)

 /* Break-points
  -------------------------- */
 @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 ,会使用默认值 map,会使用默认值map,会使用默认值–breakpoints

 /* Break-point
 --------------------------*/
 $--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)
 );
 ​