上一篇文章咱們分析了 Layout 佈局組件的設計和實現,它的應用場景一般是局部佈局。對於整個頁面的佈局,element-ui
提供了 Container
佈局容器組件,專門用於 PC 管理後臺頁面的總體佈局。javascript
咱們先經過幾幅圖看一下頁面的常見佈局。css
這兩張圖的佈局在後臺系統中很常見,經過簡單的 CSS 就能夠實現。不過咱們更喜歡用組件化的開發方式,把這些 CSS 的細節封裝到組件裏,以下:html
<el-container>
<el-header>Header</el-header>
<el-main>Main</el-main>
<el-footer>Footer</el-footer>
</el-container>
<el-container>
<el-header>Header</el-header>
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-container>
<el-main>Main</el-main>
<el-footer>Footer</el-footer>
</el-container>
</el-container>
</el-container>
複製代碼
咱們使用了 <el-container>
、<el-header>
、<el-main>
、<el-footer>
、<el-aside>
5 種組件,來看一下它們各自的做用:前端
<el-container>
:外層容器。當子元素中包含 <el-header>
或 <el-footer>
時,所有子元素會垂直上下排列,不然會水平左右排列。java
<el-header>
:頂欄容器。node
<el-aside>
:側邊欄容器。element-ui
<el-main>
:主要區域容器。sass
<el-footer>
:底欄容器。ide
這幾個組件須要遵循以下約束:組件化
<el-container>
的子元素只能是後四者,後四者的父元素也只能是<el-container>
。
瞭解了 element-ui
Container 佈局容器組件的需求後,咱們來分析它的設計和實現。
針對圖示的佈局,咱們能夠經過 flex 的佈局方式輕鬆實現,element-ui
也是基於 flex 佈局實現的,接下來咱們來分析各個組件的實現。
先來看模板部分:
<template>
<section class="el-container">
<slot></slot>
</section>
</template>
複製代碼
<el-container>
組件會渲染成一個 <section>
標籤,並經過 slot
作內容分發。
再來看一下 CSS 部分:
@include b(container) {
display: flex;
flex-direction: row;
flex: 1;
flex-basis: auto;
box-sizing: border-box;
min-width: 0;
}
複製代碼
重點看一下 CSS 部分,display:flex
建立了一個 flex 容器,flex-direction:row
指定了內部元素是在水平方向排列。這裏爲何還有 flex:1
呢,由於 <el-container>
容器是支持嵌套的,而且咱們知道 flex:1
至關於 flex-grow:1;flex-shrink:1;flex-basis:0
,也就是當 <el-container>
被嵌套的時候,它會佔滿剩餘空間。flex-basis:auto
表示分配空間以前會先跟父容器預定自身內容大小的空間,而後剩下的纔會納入到剩餘空間。
<el-container>
容器默認是水平排列,固然也須要支持垂直排列,咱們能夠給組件提供一個 direction
的 prop
,若是傳入的 direction
是 vertical
,添加對應的 CSS。
<template>
<section class="el-container" :class="{ 'is-vertical': isVertical }">
<slot></slot>
</section>
</template>
複製代碼
export default {
name: 'ElContainer',
componentName: 'ElContainer',
props: {
direction: String
},
computed: {
isVertical() {
if (this.direction === 'vertical') {
return true;
} else if (this.direction === 'horizontal') {
return false;
}
}
}
};
複製代碼
@include when(vertical) {
flex-direction: column;
}
複製代碼
若是傳入的 direction
爲 vertical
,則添加 is-vertical
的 CSS,最終經過修改 flex-direction:column
實現內部元素是在垂直方向排列。
回顧前面的一個需求:當 <el-container>
容器的子元素中包含 <el-header>
或 <el-footer>
時,所有子元素會垂直上下排列,不然會水平左右排列。
實現它也很容易,擴展計算屬性 isVertical
的判斷條件便可:
computed: {
isVertical() {
if (this.direction === 'vertical') {
return true;
} else if (this.direction === 'horizontal') {
return false;
}
return this.$slots && this.$slots.default
? this.$slots.default.some(vnode => {
const tag = vnode.componentOptions && vnode.componentOptions.tag;
return tag === 'el-header' || tag === 'el-footer';
})
: false;
}
}
複製代碼
這裏用了一個小技巧,this.$slots.default
獲取的是默認插槽中的全部 vnodes
節點,而後對他們遍歷,經過 vnode.componentOptions.tag
來判斷這個 vnode
是否是 <el-header>
或者是 <el-footer>
。vnode.componentOptions
並不在官網 API 裏,可是對於熟讀 Vue 源碼的人來講並不陌生。
先來看一下模板部分:
<template>
<header class="el-header">
<slot></slot>
</header>
</template>
複製代碼
<el-header>
組件會渲染成一個 <header>
標籤,並經過 slot
作內容分發。
再來看一下 CSS 部分:
@include b(header) {
padding: $--header-padding;
box-sizing: border-box;
flex-shrink: 0;
}
複製代碼
其中 $--header-padding
是一個變量,在 packages/theme-chalk/src/common/var.scss
文件中定義。flex-shrink: 0
表示即便空間不夠,也不會縮小 <el-header>
所佔空間。
一般來講頭部都會有一個固定高度,所以 <el-header>
容許你傳入一個 height
的 props
來指定高度,若是不指定的話提供一個默認高度。
<template>
<header class="el-header" :style="{ height }">
<slot></slot>
</header>
</template>
複製代碼
export default {
name: 'ElHeader',
componentName: 'ElHeader',
props: {
height: {
type: String,
default: '60px'
}
}
};
複製代碼
因爲直接經過 :style
設置的樣式,因此這裏傳入高度的時候必定要攜帶單位。
先來看一下模板部分:
<template>
<main class="el-main">
<slot></slot>
</main>
</template>
複製代碼
<el-main>
組件會渲染成一個 <main>
標籤,並經過 slot
作內容分發。
再來看一下 CSS 部分:
@include b(main) {
// IE11 supports the <main> element partially https://caniuse.com/#search=main
display: block;
flex: 1;
flex-basis: auto;
overflow: auto;
box-sizing: border-box;
padding: $--main-padding;
}
複製代碼
注意,<main>
標籤在 IE11 中是部分支持的。一般 <main>
中包裹的內容徹底由它的子元素來決定,因此並不會設置高和寬,只是經過 flex:1
來分配 <el-container>
容器的剩餘空間。
先來看一下模板部分:
<template>
<footer class="el-footer">
<slot></slot>
</footer>
</template>
複製代碼
<el-footer>
組件會渲染成一個 <footer>
標籤,並經過 slot
作內容分發。
再來看一下 CSS 部分:
@include b(footer) {
padding: $--footer-padding;
box-sizing: border-box;
flex-shrink: 0;
}
複製代碼
和頭部同樣,一般底部也會有一個固定高度,所以 <el-footer>
容許你傳入一個 height
的 props
來指定高度,若是不指定的話提供一個默認高度。
<template>
<footer class="el-footer" :style="{ height }">
<slot></slot>
</footer>
</template>
複製代碼
export default {
name: 'ElFooter',
componentName: 'ElFooter',
props: {
height: {
type: String,
default: '60px'
}
}
};
複製代碼
先來看一下模板部分:
<template>
<aside class="el-aside">
<slot></slot>
</aside>
</template>
複製代碼
<el-aside>
組件會渲染成一個 <aside>
標籤,並經過 slot
作內容分發。
再來看一下 CSS 部分:
@include b(aside) {
overflow: auto;
box-sizing: border-box;
flex-shrink: 0;
}
複製代碼
<el-aside>
組件用來渲染側邊欄,而側邊欄一般會有寬度,所以 <el-aside>
,容許你傳入一個 width
的 props
來指定寬度,若是不指定的話提供一個默認寬度。
<template>
<aside class="el-aside" :style="{ width }">
<slot></slot>
</aside>
</template>
複製代碼
export default {
name: 'ElAside',
componentName: 'ElAside',
props: {
width: {
type: String,
default: '300px'
}
}
};
複製代碼
element-ui
的 Container
佈局容器組件的實現仍是很簡單的:建立了一些語義化的標籤,利用插槽作內容分發,經過 flex 實現佈局效果。
學習完這篇文章,你能夠順便對 flex 佈局、HTML5 的語義化標籤作一下複習,加深理解,並瞭解到 Vue 源碼中的一些小技巧。
把不會的東西學會了,那麼你就進步了,若是你以爲這類文章有幫助,也歡迎把它推薦給你身邊的小夥伴。
下一篇預告 :Element-UI 技術揭祕(5)色彩、字體、邊框與圖標。
另外,我最近剛開了公衆號「老黃的前端私房菜」,《Element-UI 技術揭祕》系列文章會第一時間在公衆號更新和發佈,除此以外,我還會常常分享一些前端進階知識,乾貨,也會偶爾分享一些軟素質技能,歡迎你們關注喔~