Python

vue 实现(抄袭)一个简单的栅格组件

2019-02-27  本文已影响351人  copyLeft

vue 实现(抄袭)一个简单的栅格组件

参考iview, ant-design 的栅格组件,发现两者的基础思路是一致的。
这里通过实现一个简化版的栅格组件做总结.

目标

基本原理

栅格组件,可以看成对横向宽度的分割。
例如:实现左右平分布局


// html
<div calss="container">
    <div class="left" />
    <div class="right" />
</div>

// css
.contianer{
    
    display: flex;
    justify-content: space-between;
    
}

.left,
.right{
    flex: 0 0 auto;
    width: 50%;
    height: 100px;
}

.left{
    background: blue;
}

.right{
    background: orange;
}

实现栅格组件的基本思路与二等分基本一致,既是根据 Col组件不同的 span 值,动态的切换对应的等分样式, 所以重点在css配置及如何切换

实现

这里基础布局会借用flex, css使用less编写,组件样式使用前缀做区分。

Row 基本结构


    <template>
        <div :class='classList' :style='style'>
            // 内容插槽
            <slot></slot>
        </div>
    </template>
    
    <script lang='ts'>
    
    import { Vue, Component, Prop, Provide } from 'vue-property-decorator'
    
    const prefix = 'b-row'
    
    @Component
    export default class Row extends Vue {
        
        // 组件类名
        get classList (){
            return [
                prefix,
            ]
        }
    
    }
    </script>

Col 基础结构

<template>
    <div :class='classList'>
        <slot></slot>
    </div>
</template>
<script lang='ts'>
import { Component, Vue, Prop, Inject,  } from "vue-property-decorator"

const prefix = "b-col"


@Component
export default class Col extends Vue {
    
    // 接口定义
    @Prop() span?:number
    @Prop() xs?:number
    @Prop() sm?:number
    @Prop() md?:number
    
    get classList() {
        return {
            [prefix]: true
        }
    }

}

布局样式

这里我们希望通过设置 Col 的 span 属性分配列组件所占比例,
占满一行 span=24.

iview ant-design 使用的后者, 猜想原因: 一方面是对性能的考虑,另一方面从实际使用上,24切分基本满足大多数情况.


@prefix-row: ~'b-row';
@prefix-col: ~'b-col';
@grid-columns: 24;


// Row 基础样式
.@{prefix-row}{
    
    position: relative;
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;
    flex-wrap: wrap;
    height: auto;
    
}

// Col 基础样式
.@{prefix-col}{

    overflow: hidden;
    flex: 0 0 auto;
    box-sizing: border-box;

}


// 创建切分样式
// 因为每一切分的样式逻辑相同, 我们可以使用函数实现

.loop-grid-column(@index, @class) when(@index > 0) {
    
    // 拼接类名 例如:b-col-span-1, 这里的 @class 主要为响应式设置不同的类名时使用 
    .@{prefix-col}-@{calss}-@{index}{
        display: block;
        box-sizing: border-box;
        width: @index / @grid-columns * 100%;
    }
    
    // 递归调用
    .loop-grid-column((@index - 1), @class)
}

// @index = 0 是的样式
.loop-grid-column(@index, @class) when(@index = 0) {

    .@{prefix-col}-@{sizeType}-@{index}{
        display: none;
    }
    
}

/*

    编译后: 
        .b-col-span-1{ ... }
        .b-col-span-2{ ... }
        .b-col-span-3{ ... }
        .b-col-span-4{ ... }
        .b-col-span-5{ ... }
        .b-col-span-6{ ... }
        .b-col-span-7{ ... }
        ....
*/


.make-grid(@class){
    .loop-grid-column(@grid-columns, @class);
}

// span 切分
.make-grid(~'span')

// 响应式
@media (max-width: 575px) {
    .makeGrid(~'xs')
}

@media (min-width: 576px) {
    .makeGrid(~'sm')
}

@media (min-width: 768px) {
    .makeGrid(~'md')
}

@media (min-width: 992px) {
    .makeGrid(~'lg')
}

// 这里需要注意样式的先后顺序, 保证大尺寸样式覆盖小尺寸

设置动态类名

现在我们需要根据 span 或 响应配置 动态的设置 Col 的类名,以对应到不同的css样式上.



// col-html
    <div :class='classList'>
        <slot></slot>
    </div>

// col-ts

 get classList(){
        
        let vm:any = this;
        let { span, xs, sm, md } = this;
        
        // 切分接口类型
        let sizeType:string[] = ['xs', 'sm', 'md', 'span']
        let typeClass = { [prefix]: true};
        
        sizeType.forEach((type: string) =>{

            let size = <number|undefined>(vm[type]);
            let temp = {}
            
            // 如果切分类型存在值, 拼接对应的类名
            if(typeof size === 'number'){
                temp = {
                    [`${prefix}-${type}-${size}`]: true 
                }
            }
            
            // 合并类名
            typeClass = Object.assign(typeClass, temp)
            
        })
            
        return typeClass
    }
    
    /* 
        例如:
        
        {
            b-col-span-10: true,
            b-col-xs-24: true,
            b-col-sm-12: true
        }
    */

完整例子

Row.vue


<template>
    <div :class='classList' :style='style'>
        <slot></slot>
    </div>
</template>
<script lang='ts'>
import { Vue, Component, Prop, Provide } from 'vue-property-decorator'

const prefix = 'b-row'

@Component
export default class Row extends Vue {

    @Provide() rowContext = this
    @Prop({default: 0}) gutter!:number
    
    private time:Date = new Date()

    // computed
    get classList (){
        return [
            prefix,
        ]
    }

    get style (){

        return this.gutter && this.gutter > 0 ? {
            'margin-left': `-${this.gutter/2}px`,
            'margin-right': `-${this.gutter/2}px`
        } : {}
    }
  
}
</script>

Col.vue


<template>
    <div :class='classList' :style='style'>
        <slot></slot>
    </div>
</template>
<script lang='ts'>
import { Component, Vue, Prop, Inject,  } from "vue-property-decorator"

const prefix = "b-col"


@Component
export default class Col extends Vue {

    @Inject() rowContext?:any
    @Prop() span?:number
    @Prop() xs?:number
    @Prop() sm?:number
    @Prop() md?:number
    
    
    @Prop() order?:number
    
    
    get classList(){
        
        let vm:any = this;
        let { span, xs, sm, md } = this;
        let sizeType:string[] = ['xs', 'sm', 'md', 'span']
        let typeClass = { [prefix]: true};
        
        sizeType.forEach((type: string) =>{

            let size = <number|undefined>(vm[type]);
            let temp = {}
            
            if(typeof size === 'number'){
                temp = {
                    [`${prefix}-${type}-${size}`]: true 
                }
            }
            
            typeClass = Object.assign(typeClass, temp)
            
        })
                
        return typeClass
    }


    
    get style(){

        let order = this.order;
        let gutter = this.rowContext.gutter;
        let baseStyle = {
            'order': `${order !== undefined ? order : 0 }`
        };

        let pdStyle = {};

        if(gutter > 0){
            pdStyle =  {
                'padding-left': `${gutter/2}px`,
                'padding-right': `${gutter/2}px`,
            }
        }

        return Object.assign({}, baseStyle, pdStyle);
    }
   
}

</script>

index.less



@prefix-row: ~'b-row';
@prefix-col: ~'b-col';
@gird-columns: 24;

.loop-grid-columns(@index, @sizeType) when (@index > 0){

    .@{prefix-col}-@{sizeType}-@{index}{
        display: block;
        box-sizing: border-box;
        width: @index / @gird-columns * 100%;
    }
    
    .loop-grid-columns((@index - 1), @sizeType)
}

.loop-grid-columns(@index, @sizeType) when (@index = 0){

    .@{prefix-col}-@{sizeType}-@{index}{
        display: none;
    }
    
}

.clearfix{
    &::after{
        content: '';
        display: table;
    }
    clear: both;
}
.mackRow(@gutter:0){
    .clearfix;
    
    position: relative;
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;
    flex-wrap: wrap;

    margin-left: (@gutter/-2)px;
    margin-right: (@gutter/-2)px;
    height: auto;

}

.@{prefix-col}{

    overflow: hidden;
    flex: 0 0 auto;
    box-sizing: border-box;

}


// flex 水平布局
@justify: center, flex-start, flex-end, space-between, space-around;
.loop-flex-justify(@i: 1) when (@i < length(@justify) + 1) {

    @type: extract(@justify, @i);
    @prop: ~'[justify="@{type}"]';
    &@{prop}{
        justify-content: @type;
    }

    .loop-flex-justify(@i + 1)
}

// flex 垂直布局
@align: flex-start, flex-end, center, baseline, stretch;
.loop-flex-align(@i: 1) when (@i < length(@align) + 1){
    @type: extract(@align, @i);
    @prop: ~'[align="@{type}"]';
    
    &@{prop}{
        align-items: @type;
    }

    .loop-flex-align((@i + 1))
}


.@{prefix-row}{
    .mackRow();
    box-sizing: border-box;
    background: #eee;  

    .loop-flex-justify();
    .loop-flex-align();
}


.makeGrid(@class){
    .loop-grid-columns(@gird-columns, @class);
}

.makeGrid(~'span');

@media (max-width: 575px) {
    .makeGrid(~'xs')
}

@media (min-width: 576px) {
    .makeGrid(~'sm')
}

@media (min-width: 768px) {
    .makeGrid(~'md')
}

@media (min-width: 992px) {
    .makeGrid(~'lg')
}



上一篇下一篇

猜你喜欢

热点阅读