Skip to content
On this page

element的row组件

组件用法

html
<el-row gutter='20' type='flex' justify='center'>
  </el-row>
  • gutter: 指定内部的分栏的间隔
  • 默认占满全行 (div未设置宽度时,就是占满全行的)
  • type='flex': 指定为flex布局
  • justify: 可以指定弹性布局的对齐方式,指定 start, center, end, space-between, space-around 其中的值来定义子元素的排版方式。
  • align: 可以指定弹性布局的垂直对齐方式

猜测其实现原理:默认type为grid布局,gutter对应gap,设置宽度为100%,如果指定type为flex,则用justify设置样式justify-content的属性值。

查看源码

javascript
props: {
    tag: {
      type: String,
      default: 'div'
    },
    gutter: Number,
    type: String,
    justify: {
      type: String,
      default: 'start'
    },
    align: String
  },
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 ? `is-align-${this.align}` : '',
        { 'el-row--flex': this.type === 'flex' }
      ],
      style: this.style
    }, this.$slots.default);
  }

源码分析

javascript
// 主要实现的函数:render(h)
render(h) {
    return h(this.tag, {
      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);
  }
  // this.tag === 'div'
  /*
  {
      class: [
        'el-row',
        this.justify !== 'start' ? `is-justify-${this.justify}` : '',
        // 如果指定了justify,类名就为is-justify-${this.justify},然后根据css修改布局
        this.align ? `is-align-${this.align}` : '',
        { 'el-row--flex': this.type === 'flex' }
        // 如果this.type指定了flex,则类名有el-row--flex
      ],
      style: this.style //用计算属性,计算gutter/2为margin-left和margin-right,返回对象直接给style属性
    }
  */
  • 基本实现的思路掌握后,我们看下css文件
css
/*is-justify-${this.justify}*/
/*is-align-${this.align}*/
/*el-row--flex*/
/*这三个类名分别控制水平排布,文本对齐方式,布局方式*/
/*el-row--flex*/
.el-row {
    position: relative;
    /*猜测是给el-col的span布局用*/
    -webkit-box-sizing: border-box;
    box-sizing: border-box
}

/*以下是清除浮动用的*/
.el-row::after,
.el-row::before {
    display: table;
    content: ""
}

.el-row::after {
    clear: both
}
/*flex布局,带兼容性*/
.el-row--flex {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex
}

/*is-justify-${this.justify}*/
.el-row--flex.is-justify-center {
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center
}

.el-row--flex.is-justify-end {
    -webkit-box-pack: end;
    -ms-flex-pack: end;
    justify-content: flex-end
}

.el-row--flex.is-justify-space-between {
    -webkit-box-pack: justify;
    -ms-flex-pack: justify;
    justify-content: space-between
}

.el-row--flex.is-justify-space-around {
    -ms-flex-pack: distribute;
    justify-content: space-around
}

/*is-align-${this.align}*/
.el-row--flex.is-align-top {
    -webkit-box-align: start;
    -ms-flex-align: start;
    align-items: flex-start
}

.el-row--flex.is-align-middle {
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center
}

.el-row--flex.is-align-bottom {
    -webkit-box-align: end;
    -ms-flex-align: end;
    align-items: flex-end
}

el-row总结

  • 默认未使用弹性布局
    • div未设置宽度时, 默认占满全行,高度由内容决定.
  • 如果指定flex,则开启弹性布局,
    • 并且可以通过justify``和align(基本是flex布局的缩写)设置对齐方式`.
  • 使用render函数,
    • 通过配置class,设置基本样式和flex布局.
    • 通过计算属性计算gutter并且设置marginLeftmarginRight返回个对象给配置项style
    • 通过vue提供的匿名插槽API,this.$slot.default指定该组件的内容为匿名插槽内容.
    • 可以猜测: 里面放的col组件内容,是由匿名插槽设计的

疑问?

  • 为何设置自身的margin, 可以达到设置分栏(col组件)的margin的目的?
  • 猜测: col组件接收了row组件的margin值.

element的col组件

组件用法

html
<el-row>
  <el-col :span="24"><div class="grid-content bg-purple-dark"></div></el-col>
</el-row>
<el-row :gutter="20">
  <el-col :span="6"><div class="grid-content bg-purple"></div></el-col>
  <el-col :span="6"><div class="grid-content bg-purple"></div></el-col>
  <el-col :span="6"><div class="grid-content bg-purple"></div></el-col>
  <el-col :span="6"><div class="grid-content bg-purple"></div></el-col>
</el-row>
<el-row :gutter="20">
  <el-col :span="16"><div class="grid-content bg-purple"></div></el-col>
  <el-col :span="8"><div class="grid-content bg-purple"></div></el-col>
</el-row>
<el-row :gutter="20">
  <el-col :span="6"><div class="grid-content bg-purple"></div></el-col>
  <el-col :span="6" :offset="6"><div class="grid-content bg-purple"></div></el-col>
</el-row>
<el-row :gutter="20">
  <el-col :span="6" :offset="6"><div class="grid-content bg-purple"></div></el-col>
  <el-col :span="6" :offset="6"><div class="grid-content bg-purple"></div></el-col>
</el-row>
<el-row :gutter="10">
  <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"><div class="grid-content bg-purple"></div></el-col>
  <el-col :xs="4" :sm="6" :md="8" :lg="9" :xl="11"><div class="grid-content bg-purple-light"></div></el-col>
  <el-col :xs="4" :sm="6" :md="8" :lg="9" :xl="11"><div class="grid-content bg-purple"></div></el-col>
  <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"><div class="grid-content bg-purple-light"></div></el-col>
</el-row>
  • span: 设置col组件占用父亲的比例, 最大为24
  • offset: 设置col组件向左偏移比例,最大为24
  • xs\sm\md\lg\xl: 设置col组件的响应式尺寸

猜测其实现原理: span设置该组件的宽度百分比或者rem, 继承父亲的。offset 利用margin或者padding实现的。 xs\sm\md\lg\xl 通过设置rem

查看源码

javascript
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;
      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'].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);
  }

源码分析

javascript
// 主要实现的方法,还是用render()函数
return h(this.tag, {
    class: ['el-col', classList],
    style
}, this.$slots.default);
// tag还是一样,默认用div

// class里面主要是classList
['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]}`
      );
    }
});
// 遍历span和offset组成的数组。
// 如果使用组件时,如果传递了对应的props
// 并且遍历不是span时,将 `el-col-${prop}-${this[prop]}`  push进classList
// 如果遍历到span时,将`el-col-${this[prop]}` push进classList
// 所以span是一个单独的样式,el-col-xxx, 其他的是:例如 el-col-offset-xxx, xxx为传入的props值

  
// 下面是如何用父组件row的margin,来设置子组件col的间距的
if (this.gutter) {
  style.paddingLeft = this.gutter / 2 + 'px';
  style.paddingRight = style.paddingLeft;
}
// 使用col自身的gutter属性,设置内边距,paddingLeft和paddingRight
// 疑问?? col自身哪里来的gutter属性?
// 解决:col的计算属性gutter
computed: {
    gutter() {
      // vue提供的钩子,this.$parent拿到父节点
      let parent = this.$parent;
      while (parent && parent.$options.componentName !== 'ElRow') {
        // 如果有父节点,并且父节点不是row组件,
        // 再往上找一层父节点
        parent = parent.$parent;
      }
      // 如果找到父节点,或者就是row, 返回父节点的gutter
      // 父节点的gutter(就是marginLeft和margintRight组成的对象)
      return parent ? parent.gutter : 0;
    }
},
// 所以是找父节点的计算属性gutter,返回过来的。
// 为何要找row的gutter?而不是自己设置自己的gutter?
// 大部分情况下,会写多个col组件,如果都自己设置,非常麻烦。
  • 基本实现思路已经清晰,我们看下css文件是如何设置的
css
/*先看span的样式 el-col-xxx */
/*`el-col-${this[prop]}`*/
/*也就是:span='24', 则为el-col-24*/
.el-col-1 {
    width: 4.16667%
}
.el-col-24 {
    width: 100%
}
.el-col-0 {
    display: none;
    width: 0%
}
/*可以看到,确实用百分比设置的,最大24. 
另外一旦传入0,会不显示*/
[class*=el-col-] {
    float: left;
    -webkit-box-sizing: border-box;
    box-sizing: border-box
}
/*注意,设置了所有的col: 怪异盒子,防止padding撑起盒子,
  另外设置了左浮动, 这个左浮动我们最后讲解*/
/*还记得,row里面,用了清除浮动的方法,防止高度塌陷。*/

/*然后我们别的属性,非span的情况,el-col-offset-xxx
`el-col-${prop}-${this[prop]}`*/
/*也就是:offset='6', 则为el-col-offset-6*/
.el-col-offset-1 {
    margin-left: 4.16667%
}
.el-col-offset-24 {
    margin-left: 100%
}
.el-col-offset-0 {
    margin-left: 0
}
/*
  所以offset和span的比例是一样的,设计好的百分比
  当offset传入0的时候,等于设置没有左边距
*/

/*默认样式里设置了float:left,
以及row组件的样式中设置了清除浮动*/
/*row组件的css代码,上面可见*/
/*
/*以下是清除浮动用的*/
.el-row::after,
.el-row::before {
    display: table;
    content: ""
}

.el-row::after {
    clear: both
}
*/

/*下面是push和pull的实现*/
.el-col-push-1 {
    left: 4.16667%
}

.el-col-push-24 {
    left: 100%
}

.el-col-pull-1 {
    right: 4.16667%
}

.el-col-pull-24 {
    right: 100%
}
/*因此,它其实是给pull和push用的,
push是设置浮动left的距离,pull是设置浮动right的距离*/
/*所以有种push是往右推,pull是往左拉的感觉*/

el-col总结

  • 边距实现:

    • 设置自己的gutter计算属性。里面:通过判断this.$parent拿到row组件gutter计算属性。
    • 它是一个{marginLeft: xxx, marginRight: xxx}组成的对象。
    • render()函数里面,判断自己的gutter计算属性是否有值,返回给局部变量style对象
      • 即用户有没有在row设置gutter.
      • 有值就将值(margin) 设置为style对象的paddingLeft和paddingRight
    • 渲染时,配置项style接收style对象
  • span实现:

    • render()函数里面,遍历各个字符串(spanoffset)。为了便于给局部变量:classList数组 添加类名
    • 如果col组件传入了对应的字符串组成的props,就开始判断
      • 如果传入了span,就添加el-col-${this[prop]}进classList。
      • (例如::span='12', 则就是el-col-${this[span]} 也就是 el-col-12)
      • el-col-xxx就是使用span控制的样式,css设置宽度百分比实现的。
    • 渲染时,配置项'class'接收一个数组,由默认样式和classList局部变量组成 ['el-col',classList]
  • offset实现:

    • 紧接着上面,遍历判断:
      • 如果传入的是非span,就添加el-col-${prop}-${this[prop]}进classList。
      • 例如::offset=12, 则就是el-col-offset-${this[offset]} 也就是el-col-offset-12
      • el-col-offset-xxx就是offset控制的样式, css设置margin-left来实现的。
  • pull和push实现:

    • 判断部分与上面一样, el-col-pull-xxxel-col-push-xxx主要看css部分
      • css上来给所有el-col-开头的,设置了float:left,左浮动
      • 然后给el-col-pull-xxx 设置right百分比, 目的是模拟的效果。
      • el-col-push-xxx 设置left百分比, 目的是模拟的效果。
      • 并且,还记得row组件样式里,有用伪元素清除浮动的css代码。

一个陪你成长的前端博客 - XDocs