Element 2 組件源碼剖析之布局容器
- 2022 年 7 月 27 日
- 筆記
- 0x02.FrontEnd, element, VUE
0x00 簡介
前文分析過組件的 布局柵格化(Grid Layout) ,通過基礎的 24 分欄,迅速簡便地創建布局。
本文將介紹用於布局的容器組件,使用 Flexbox
功能將其所控制區域設定為特定的布局,方便快速搭建頁面的基本結構。本文將深入分析組件源碼,剖析其實現原理,耐心讀完,相信會對您有所幫助。 組件文檔
更多組件剖析詳見 👉 📚 Element 2 源碼剖析組件總覽 。
0x01 布局容器
布局容器提供5個組件,支援多層嵌套,方便快速搭建頁面的基本結構:
<el-container>
:布局容器,其下可嵌套<el-header>
<el-footer>
<el-aside>
<el-main>
或<el-container>
本身,可以放在任何父容器中。當子元素中包含<el-header>
或<el-footer>
時,全部子元素會垂直上下排列,否則會水平左右排列。<el-header>
:頂部容器,其下可嵌套任何元素,只能放在<el-container>
中。<el-aside>
:側邊欄容器,其下可嵌套任何元素,只能放在<el-container>
中。<el-main>
:主要區域容器,其下可嵌套任何元素,只能放在<el-container>
中。<el-footer>
:底部容器,其下可嵌套任何元素,只能放在<el-container>
中。
以上組件採用了 flex 布局,使用前請確定目標瀏覽器是否兼容。此外,
<el-container>
的子元素只能是後四者,後四者的父元素也只能是<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>
0x02 程式碼實現
container 布局容器
組件 container
封裝了 <section>
元素,包含沒有後備內容(默認值)的匿名插槽 。組件定義了direction
的 prop 屬性,用於子元素的排列方向。
// packages\container\src\main.vue
<template>
<section class="el-container" :class="{ 'is-vertical': isVertical }">
<slot></slot>
</section>
</template>
<script>
export default {
name: 'ElContainer',
componentName: 'ElContainer',
props: {
direction: String
},
computed: {
isVertical() {
// ...
}
}
};
</script>
若沒有定義了direction
屬性值,組件通過tag
判斷子元素中包含 <el-header>
或 <el-footer>
時,全部子元素會垂直上下排列,否則會水平左右排列。 componentOptions類型定義
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;
}
}
header 頂部容器
組件 header
封裝了 <header>
元素,包含一個 slot
。組件定義了height
的 prop 屬性,設置頂部容器高度,默認值60px
。
// packages\header\src\main.vue
<template>
<header class="el-header" :style="{ height }">
<slot></slot>
</header>
</template>
<script>
export default {
name: 'ElHeader',
componentName: 'ElHeader',
props: {
height: {
type: String,
default: '60px'
}
}
};
</script>
aside 側邊欄容器
組件 aside
封裝了 <aside>
元素,包含一個 slot
。組件定義了width
的 prop 屬性,設置側邊欄寬度,默認值300px
。
// packages\aside\src\main.vue
<template>
<aside class="el-aside" :style="{ width }">
<slot></slot>
</aside>
</template>
<script>
export default {
name: 'ElAside',
componentName: 'ElAside',
props: {
width: {
type: String,
default: '300px'
}
}
};
</script>
main 主要區域(內容)容器
組件 main
封裝了 <main>
元素,包含一個 slot
。
// packages\main\src\main.vue
<template>
<main class="el-main">
<slot></slot>
</main>
</template>
<script>
export default {
name: 'ElMain',
componentName: 'ElMain'
};
</script>
footer 底部容器
組件 footer
封裝了 <footer>
元素,包含一個 slot
。組件定義了height
的 prop 屬性,設置頂部容器高度,默認值60px
。
// packages\footer\src\main.vue
<template>
<footer class="el-footer" :style="{ height }">
<slot></slot>
</footer>
</template>
<script>
export default {
name: 'ElFooter',
componentName: 'ElFooter',
props: {
height: {
type: String,
default: '60px'
}
}
};
</script>
0x03 組件樣式
組件樣式源碼使用 scss
的混合指令 b
、 when
嵌套生成組件樣式。
// packages\theme-chalk\src\common\var.scss
$--header-padding: 0 20px !default;
$--footer-padding: 0 20px !default;
$--main-padding: 20px !default;
// packages\theme-chalk\src\container.scss
@include b(container) {
display: flex;
flex-direction: row;
flex: 1;
flex-basis: auto;
box-sizing: border-box;
min-width: 0;
@include when(vertical) {
flex-direction: column;
}
}
// packages\theme-chalk\src\header.scss
@include b(header) {
padding: $--header-padding;
box-sizing: border-box;
flex-shrink: 0;
}
// packages\theme-chalk\src\footer.scss
@include b(footer) {
padding: $--footer-padding;
box-sizing: border-box;
flex-shrink: 0;
}
// packages\theme-chalk\src\main.scss
@include b(main) {
// IE11 supports the <main> element partially //caniuse.com/#search=main
display: block;
flex: 1;
flex-basis: auto;
overflow: auto;
box-sizing: border-box;
padding: $--main-padding;
}
// packages\theme-chalk\src\aside.scss
@include b(aside) {
overflow: auto;
box-sizing: border-box;
flex-shrink: 0;
}
使用 gulpfile.js
編譯 scss
文件轉換為CSS
,經過瀏覽器兼容、格式壓縮,最後生成樣式內容如下。
/* packages\theme-chalk\lib\container.css */
.el-container {
display: flex;
flex-direction: row;
flex: 1;
flex-basis: auto;
box-sizing: border-box;
min-width: 0;
}
.el-container.is-vertical {
flex-direction: column;
}
/* packages\theme-chalk\lib\main.css */
.el-main {
display: block;
flex: 1;
flex-basis: auto;
overflow: auto;
box-sizing: border-box;
padding: 20px;
}
/* packages\theme-chalk\lib\aside.css */
.el-aside {
overflow: auto;
box-sizing: border-box;
flex-shrink: 0;
}
/* packages\theme-chalk\lib\header.css */
.el-header {
padding: 0 20px;
box-sizing: border-box;
flex-shrink: 0;
}
/* packages\theme-chalk\lib\footer.css */
.el-footer {
padding: 0 20px;
box-sizing: border-box;
flex-shrink: 0;
}
容器布局實現使用 CSS Flexbox,flex-basis
、flex-shrink
、flex
等屬性的語法內容請閱讀 Flex 布局教程:語法篇 阮一峰。
前文曾提到<el-container>
的子元素只能是後四者,後四者的父元素也只能是 <el-container>
。因為 只有container
組件指定為 Flex 布局,其餘組件若是想要Flex 布局生效,只能將組件作為 container
的子組件,當然 container
可以自包含。
0x04 關注專欄
此文章已收錄到專欄中 👇,可以直接關注。