# 布局篇

# 设计细节

  1. row组件默认采用flex布局,可通过leftcenterright来设置对齐方式;
  2. row组件可通过设置gutter参数来设置列之间的间隔;
  3. col组件可通过设置span参数来设置不同列数,默认将宽度分为24列,同时可搭配offset参数来设置分栏偏移数;
  4. 响应式布局传递的参数不采用element-ui那些xssm参数,字面上不利于理解, 所以这里采用了phoneiPadnarrowPCwidePC等参数来分别表示在屏幕大小为 手机、平板、窄屏PC端和宽屏PC端下的展示。

# 功能处理细节

  1. span设置分栏数。默认将页面宽度分成了24等分,再通过span/24来算出分栏的占比,那要怎么设置各占比分栏宽度呢?如果直接通过yv-col-1 ~ 24这样写24个类名,就会出现一堆重复的代码,维护、改动也麻烦。幸好可以通过css预处理器来处理这种情况,预处理器可以像JS那样写变量,函数,可以为我们提供很多方便的操作,这里采用了scss来处理:
@for $n from 1 through 24 {
  $class-prefix: yv-col-;
  &.#{$class-prefix}#{$n} {
    width: ($n / 24) * 100%
  }
}

先设置通用前缀yv-col-,再通过for循环从1到24,算出每一份的宽度,这样就可以根据span的值得到对应的类名yv-col-n从而得到对应的宽度。包括offset设置偏移栏数也是如此:

@for $n from 1 through 24 {
  $class-prefix:yv-col-offset-;
  &.#{$class-prefix}#{$n} {
    margin-left: ($n / 24) * 100%
  }
}
  1. gutter传参问题。设置分栏间隔是通过在row组件上面传递gutter参数,但实际上我们还得靠处理col组件来实现。一开始直接像下面这样直接把gutter传个每个col组件:
<yv-row :gutter="10">
  <yv-col :gutter="10"></yv-col>
  <yv-col :gutter="10"></yv-col>
</yv-row>

看起来很不友好。这里可以使用父子组件通信的另一种方法:$parent$children。如下:

<!-- Row.vue -->
mounted() {
  this.$children.forEach(child => {
    child.gutter = this.gutter
  })
}

<!-- Col.vue -->
data() {
  return {
    gutter: 0
  }
}

row的生命周期mounted中通过$children给每个子组件的gutter属性赋值,同时col组件也要在data里面定义gutter属性。

  1. gutter具体设置问题。一开始的想法是直接给每一列的左右加上gutter/2的外边距,但是这会跟通过offset设置的偏移分栏外边距产生冲突。那么换成内边距如何呢?试下内边距的效果。 设置内边距效果 从图片可以发现最左边跟最右边的分栏没有紧挨父级容器,这是设置了内边距的原因,跟父容器的空隙就是gutter/2px。解决方案是给父级容器加上-gutter/2的左右外边距,就ok了。

  2. 响应式处理。这里采用了phoneiPadnarrowPCwidePC这种直观的字段来表示不同屏幕宽度。通过createClasses方法来创建不同的类名:

computed: {
  colClass() {
    let {
      span,
      offset,
      phone,
      iPad,
      narrowPC,
      widePC
    } = this
    return [
      'yv-col',
      ...this.createClasses(phone, 'phone-'),
      ...this.createClasses(iPad, 'iPad-'),
      ...this.createClasses(narrowPC, 'narrowPC-'),
      ...this.createClasses(widePC, 'widePC-'),
      ...this.createClasses({ span, offset })
    ]
  }
},

methods: {
  createClasses(obj, str = '') {
    if (!obj) return []
    let arr = []
    if (obj.span) {
      arr.push(`yv-col-${str}${obj.span}`)
    }
    if (obj.offset) {
      arr.push(`yv-col-${str}offset-${obj.offset}`)
    }
    return arr
  }
}

设置一个计算属性colClass,根据传入不同的尺寸属性生成不同的class添加到col上面,再通过媒体查询的写法,来实现不同尺寸下的宽度计算:

@media (min-width: 0px) {
  @for $n from 1 through 24 {
    $class-prefix:yv-col-phone-;
    &.#{$class-prefix}#{$n} {
      width: ($n / 24) * 100%
    }
  }
  @for $n from 1 through 24 {
    $class-prefix:yv-col-phone-offset-;
    &.#{$class-prefix}#{$n} {
      margin-left: ($n / 24) * 100%
    }
  }
}
@media (min-width: 577px) {
  // ...
}
@media (min-width: 769px) {
  // ...
}
@media (min-width: 993px) {
  // ...
}
@media (min-width: 1201px) {
  // ...
}

# 人工测试

手动测试。。。已完成。

# 自动化测试

test文件夹下增加row.test.jscol.test.js文件。

row.test.js文件有3个测试用例:测试row是否存在接收getter接收align

col.test.js文件有7个测试用例:测试col是否存在接收span接收offset接收phone接收iPad接收narrowPC接收widePC

运行命令parcel watch test/* --no-cachekarma start查看测试结果:

layout测试结果

# vuepress

docs/.vuepress/components文件夹下增加多个layout-*的vue文件,内容就是我们要展示的layout示例,然后在docs/components文件夹下增加layout的md文件,内容就是放置整个layout组件说明。

具体内容请访问这里