實戰:使用 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

歡迎閱讀其它文章

相關文章
相關標籤/搜索