开发一个UI框架项目[3]-Layout

本文最后更新于:2020年9月12日 晚上

设计细节

  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来处理:

    1
    2
    3
    4
    5
    6
    @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设置偏移栏数也是如此:

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

    1
    2
    3
    4
    <yv-row :gutter="10">
    <yv-col :gutter="10"></yv-col>
    <yv-col :gutter="10"></yv-col>
    </yv-row>

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- Row.vue -->
    mounted() {
    this.$children.forEach(child => {
    child.gutter = this.gutter
    })
    }

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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上面,再通过媒体查询的写法,来实现不同尺寸下的宽度计算:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @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组件说明。

具体内容请访问这里