实战:使用 Vue3 开发一个 24 分栏的 Layout 组件

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战css

在主流的 Vue 组件库中,layout 组件是最基础的组件之一,用于快速布局页面 UI 和制做响应式系统,在这篇文章将带你着手设计并实现一个基础的 layout 组件。html

组件设计

咱们以 elementUI 为例,能够分析出 layout 组件分为两部分:rowcol,也就是行和列,row支持定义布局方式,分栏间隔和 flex 布局下的排列方式等。col支持设置栏位数和偏移量。那么基于此,咱们能够设计如下组件内容:前端

基础概念

分栏间隔

咱们将每行分为 24 栏位,也就是 24 列,分栏间隔即为每一列之间的间隙大小,这里以 px 为单位。web

布局方式

默认为普通布局,可选 flex 布局方式,方便更灵活的控制各元素的排列方式。markdown

栏位数

做用于 col 组件中,表示元素所占的列数。less

偏移量

元素默认从起始位置开始布局,若是但愿元素向左偏移必定的距离,能够传入偏移量属性控制。布局

Row 组件

组件除了支持传入栏位间隔和布局方式外,还支持定义在 flex 布局下的水平、垂直排列方式,可选值对应的是 css 属性的justify-contentalign-items的值。具体内容以下:post

name: "bp-row",
props: {
    gutter: { type: [Number, String], default: 0 }, // 分栏间隔

    type: {
      type: String,
      default: "",
      validator: function (value) {
        return ["", "flex"].indexOf(value) !== -1;
      },
    }, // 布局方式

    justify: {
      type: String,
      default: "center",
      validator: function (value) {
        return (
          ["start", "end", "center", "space-around", "space-between"].indexOf(
            value
          ) !== -1
        );
      },
    }, // flex下的水平排列方式

    align: {
      type: String,
      default: "middle",
      validator: function (value) {
        return ["top", "middle", "bottom"].indexOf(value) !== -1;
      },
    }, // flex下的垂直排列方式
}
复制代码

Col 组件

col 接收两个属性,分别是栏位数和偏移量。flex

name: "bp-col",
props: {
    span: { type: [Number, String], default: 0 }, // 栏位数
    offset: { type: [Number, String], default: 0 }, // 偏移量
},
复制代码

Row 组件的实现

首先是模板,只须要设置外层 div 便可,内容由插槽填充。以下:网站

<template>
  <div :class="clazzName" ref="row">
    <slot></slot>
  </div>
</template>
复制代码

因为组件支持两种布局方式,因此外层的样式须要额外控制,以下:

const clazzName = computed(() => {
	const isFlex = props.type === "flex";
	const prefix = isFlex ? "bp-row-flex" : "bp-row";
	const name = [prefix];

	if(isFlex){
		name.push(`bp-row-flex-justify-${props.justify}`)
		name.push(`bp-row-flex-align-${props.align}`)
	}
	return name;
});
复制代码

flex 布局下,水平、排列的样式属性须要由 props 去支持 。对应的样式以下:

.bp-row {
  position: relative;
  box-sizing: border-box;
  display: block;
}

.bp-row:after,
.bp-row:before {
  display: table;
  content: ""
}

.bp-row:after {
  clear: both
}

// flex 布局
.bp-row-flex {
  display: flex;
}

.bp-row-flex-justify-start {
  justify-content: flex-start;
}

.bp-row-flex-justify-end {
  justify-content: flex-end;
}

.bp-row-flex-justify-center {
  justify-content: center;
}

.bp-row-flex-justify-space-around {
  justify-content: space-around;
}

.bp-row-flex-justify-space-between {
  justify-content: space-between;
}

.bp-row-flex-justify-space-evenly {
  justify-content: space-evenly;
}

.bp-row-flex-align-top {
  align-items: flex-start;
}
.bp-row-flex-align-middle {
  align-items: center;
}
.bp-row-flex-align-bottom {
  align-items: flex-end;
}
复制代码

此外,针对栏位间隔的实现,须要手动获取子元素的全部 col 元素,并挨个遍历设置 padding 样式,具体实现以下:

// 设置 col 属性
const setColAttrs = () => {
	// 获取 row 下全部 col
	const row = getCurrentInstance().refs.row.children || [];
	let len = row.length;

	if (len === 0) return;

	for (let i = 0; i < len; i++) {
        // 布局模式
        row[i].classList.add("bp-col");

        // Gutter 处理
        if (props.gutter !== 0 && len > 1) {
          if (i !== 0) row[i].style.paddingLeft = `${props.gutter}px`;
          if (i !== len - 1) row[i].style.paddingRight = `${props.gutter}px`;
        }
    }
};

onMounted(() => {
	setColAttrs();
});
复制代码

Col 组件的实现

模板准备,一样,只需设置外层样式便可。以下:

<template>
  <div :class="colClassName">
    <slot></slot>
  </div>
</template>
复制代码

Col 组件的逻辑只须要处理栏位数和偏移量,生成对应的样式类便可,剩余的在样式文件中实现。

let colClassName = computed(() => {
	// 默认样式和前缀
	let prefix = "bp-col";
	let className = [];
	Number(props.span) !== 0 ? className.push(`${prefix}-${props.span}`) : "";
	
	// 偏移量
	Number(props.offset) !== 0
		? className.push(`${prefix}-offset-${props.offset}`)
		: "";
	return className;
});
复制代码

关于样式,这里使用的是 less 实现,否则的话,手动一条条去写也是能够的。

[class*=bp-col-] {
  float: left;
  box-sizing: border-box;
}

.bp-col {
  box-sizing: border-box;
}

// 总宽
@width : 100%;

// 总栏数
.total-nums(24);

.total-nums(@n, @i: 1) when (@i =< @n) {

  // ============= 分栏 ===============
  .bp-col-@{i} {
    width: @width / (24 / @i);
    display: block;
  }

  // ============= 偏移 ===============
  .bp-col-offset-@{i} {
    margin-left: @width / (24 / @i);
  }

  .total-nums(@n, (@i + 1));
}
复制代码

DEMO

<bp-row :gutter="10">
     <bp-col :span="8" title="span=8"><div class="row-demo bg-blue-5"></div></bp-col>
     <bp-col :span="16" title="span=16"><div class="row-demo bg-blue-5"></div></bp-col>
 </bp-row>
 <bp-row :gutter="10">
     <bp-col :span="3" title="span=3"><div class="row-demo bg-blue-5"></div></bp-col>
     <bp-col :span="10" title="span=10"><div class="row-demo bg-blue-5"></div></bp-col>
     <bp-col :span="11" title="span=11"><div class="row-demo bg-blue-5"></div></bp-col>
 </bp-row>
 <bp-row :gutter="10">
     <bp-col :span="4" title="span=4"><div class="row-demo bg-blue-5"></div></bp-col>
     <bp-col :span="8" title="span=8"><div class="row-demo bg-blue-5"></div></bp-col>
     <bp-col :span="3" title="span=3"><div class="row-demo bg-blue-5"></div></bp-col>
     <bp-col :span="9" title="span=9"><div class="row-demo bg-blue-5"></div></bp-col>
 </bp-row>
复制代码

image.png

欢迎阅读其它文章

相关文章
相关标签/搜索