这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战css
在主流的 Vue 组件库中,layout 组件是最基础的组件之一,用于快速布局页面 UI 和制做响应式系统,在这篇文章将带你着手设计并实现一个基础的 layout 组件。html
咱们以 elementUI 为例,能够分析出 layout 组件分为两部分:row
和col
,也就是行和列,row
支持定义布局方式,分栏间隔和 flex 布局下的排列方式等。col
支持设置栏位数和偏移量。那么基于此,咱们能够设计如下组件内容:前端
咱们将每行分为 24 栏位,也就是 24 列,分栏间隔即为每一列之间的间隙大小,这里以 px 为单位。web
默认为普通布局,可选 flex 布局方式,方便更灵活的控制各元素的排列方式。markdown
做用于 col 组件中,表示元素所占的列数。less
元素默认从起始位置开始布局,若是但愿元素向左偏移必定的距离,能够传入偏移量属性控制。布局
组件除了支持传入栏位间隔和布局方式外,还支持定义在 flex 布局下的水平、垂直排列方式,可选值对应的是 css 属性的justify-content
和align-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 接收两个属性,分别是栏位数和偏移量。flex
name: "bp-col",
props: {
span: { type: [Number, String], default: 0 }, // 栏位数
offset: { type: [Number, String], default: 0 }, // 偏移量
},
复制代码
首先是模板,只须要设置外层 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();
});
复制代码
模板准备,一样,只需设置外层样式便可。以下:
<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));
}
复制代码
<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>
复制代码