ElementUI源码分析-Row和Col组件

一、简述

  • ElementUI的row组件和col组件属于里面的layout部分,通过基础的 24 分栏,能快速的创建布局。
  • 通过 row 和 col 组件,并通过 col 组件的 span 属性我们就可以*地组合布局。

二、解读源码

  • 通过参照源码,从新实现了一遍row和col;

三、Row

  • 以下是ElementUI中的源码:
export default {
  name: 'BsRow', // 组件的name
  componentName: 'BsRow', // 组件名,主要用于会被子组件获取
  props: {
    tag: { // 通过传入的tag,来渲染不同的标签
      type: String,
      default: 'div'
    },
    gutter: Number,// 通过gutter来控制row中的元素之间的间隔
    type: String, // 控制是否以flex进行布局
    justify: { // flex元素中水平排列规则
      type: String,
      default: 'start'
    },
    align: {
      type: String,
      default: 'top'
    }
  },
  computed: {
    style() {
      const ret = {}; // 计算样式
      if(this.gutter) {
        ret.marginLeft = `${this.gutter / 2}px`;
        ret.marginRight = ret.marginLeft;
      }
      return ret;
    }
  },
  render(h) {
    // 返回dom元素
    return h(this.tag, {
      class: [
        'bs-row',
        this.justify !== 'start'? `is-justify-${this.justify}`: '',
        this.align !== 'top'? `is-align-${this.align}`: '',
        {'bs-row-flex': this.type === 'flex'}
      ],
      style: this.style
    }, this.$slots.default)
  }
}
  • 注释明确说明了传入的每个属性的用处

3.1 computed

  • computed里主要是用于计算样式
 style() {
      const ret = {}; // 计算样式
      if(this.gutter) {
        ret.marginLeft = `-${this.gutter / 2}px`;
        ret.marginRight = ret.marginLeft;
      }
      return ret;
    }
  },
  • 这段代码主要是通过gutter来设置row内部的元素间隔,因为设置了子元素的padding来控制间隔,所以首尾也会有间隔,我们通过将margin设置为负值即可解决

3.2 render函数

  • render函数主要是用于渲染dom元素的,这就是采用了jsx的写法;通过返回一个h函数来创建一个组件
render(h) {
    return h(this.tag, { // 自定义渲染标签
      class: [
        bs-row', // 组件基本样式
        this.justify !== 'start' ? `is-justify-${this.justify}` : '', // 水平排列样式名生成
        this.align !== 'top' ? `is-align-${this.align}` : '', // 垂直排列样式名生成
        { 'bs-row--flex': this.type === 'flex' } // flex布局样式
      ],
      style: this.style
    }, this.$slots.default);
}

  • 关于组件的样式主要是利用了BEM的scss写法来控制css样式

四、Col

  • col源码如下:

export default {
  name: 'BsCol', // 组件名
  props: {
    span: { // 每个组件占据的比例
      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 响应式栅格数或者栅格属性对象
  },
  computed: {
    gutter() {
      // 获取父元素, 反向递归查找第一个名为BsRow的父组件
      let parent = this.$parent;
      while(parent && parent.$options.componentName !== 'BsRow') {
        parent = parent.$parent;
      }
      return parent ? parent.gutter : 0
    }
  },
  render(h) {
    let classList = [];
    let style = {};
     // 通过父组件的gutter生成自生的padding-left与padding-right
    if(this.gutter) {
      style.paddingLeft = this.gutter / 2 + 'px';
      style.paddingRight = style.paddingLeft;
    }
  	// 这里逻辑是通过比对props对象,生成对应的CSS规则
    ['span', 'offet', 'pull', 'push'].forEach(prop => {
      if(this[prop] || this.prop === 0) {
        classList.push(
          prop !== 'span'
            ? `bs-col-${prop}-${this[prop]}`
            : `bs-col-${this[prop]}`
        )
      }
    });
    ['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => {
      if(typeof this[size] === 'object') {
        let props = this[size];
        Object.keys(props).forEach( prop => {
          classList.push(
            prop !== 'span'
              ? `bs-col-${size}-${prop}-${props[prop]}`
              : `bs-col-${size}-${props[prop]}`
          )
        })
      }
    });
    return h(this.tag, {
      class: ['bs-col', classList],
      style
    }, this.$slots.default)
  }
}
  • 同样的通过传入的值,来生成css规则,从而实现响应式
上一篇:type check failed for prop “columns“. Expected Object, got Array


下一篇:如何实现 的虚拟dom