前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue 实现一个简单的栅格组件

vue 实现一个简单的栅格组件

作者头像
copy_left
发布2019-08-30 16:04:34
2.2K0
发布2019-08-30 16:04:34
举报
文章被收录于专栏:方球方球

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

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

目标

  • 实现24格栅格布局,包括组件 Row(行), Col(列)
  • 组件可嵌套, 容器大小自适应
  • 响应式布局

基本原理

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

代码语言:javascript
复制
// 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 基本结构
代码语言:javascript
复制
    <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 基础结构
代码语言:javascript
复制
<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.

  • 一种实现是通过js计算单个组件所占的比例,设置到容器行内样式上。
  • 一种是将每一种分割类型的样式,都写入css, 通过类名的方式做区分

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

代码语言:javascript
复制
@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样式上.

代码语言:javascript
复制
// 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
代码语言:javascript
复制
<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
代码语言:javascript
复制
<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
代码语言:javascript
复制
@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')
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.02.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • vue 实现(抄袭)一个简单的栅格组件
    • 目标
      • 基本原理
        • 实现
          • 完整例子
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档