這是我參與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>
複製代碼