Element 2 组件源码剖析之布局容器

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> 

Element 2 组件源码剖析之布局容器

0x02 代码实现

container 布局容器

组件 container 封装了 <section>元素,包含没有后备内容(默认值)的匿名插槽 。组件定义了direction的 prop 属性,用于子元素的排列方向。

// packagescontainersrcmain.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

// packagesheadersrcmain.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

// packagesasidesrcmain.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 。

// packagesmainsrcmain.vue <template>   <main class="el-main">     <slot></slot>   </main> </template> <script>   export default {     name: 'ElMain',     componentName: 'ElMain'   }; </script> 

组件 footer 封装了 <footer>元素,包含一个 slot。组件定义了height的 prop 属性,设置顶部容器高度,默认值60px

// packagesfootersrcmain.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 嵌套生成组件样式。

// packagestheme-chalksrccommonvar.scss $--header-padding: 0 20px !default; $--footer-padding: 0 20px !default; $--main-padding: 20px !default;  // packagestheme-chalksrccontainer.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;   } }  // packagestheme-chalksrcheader.scss @include b(header) {   padding: $--header-padding;   box-sizing: border-box;   flex-shrink: 0; }  // packagestheme-chalksrcfooter.scss @include b(footer) {   padding: $--footer-padding;   box-sizing: border-box;   flex-shrink: 0; }   // packagestheme-chalksrcmain.scss @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; }  // packagestheme-chalksrcaside.scss @include b(aside) {   overflow: auto;   box-sizing: border-box;   flex-shrink: 0; }  

使用 gulpfile.js编译 scss 文件转换为CSS,经过浏览器兼容、格式压缩,最后生成样式内容如下。

/* packagestheme-chalklibcontainer.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; }  /* packagestheme-chalklibmain.css */ .el-main {   display: block;   flex: 1;   flex-basis: auto;   overflow: auto;   box-sizing: border-box;   padding: 20px; }  /* packagestheme-chalklibaside.css */ .el-aside {   overflow: auto;   box-sizing: border-box;   flex-shrink: 0; }  /* packagestheme-chalklibheader.css */ .el-header {   padding: 0 20px;   box-sizing: border-box;   flex-shrink: 0; }  /* packagestheme-chalklibfooter.css */ .el-footer {   padding: 0 20px;   box-sizing: border-box;   flex-shrink: 0; } 

容器布局实现使用 CSS Flexbox,flex-basisflex-shrinkflex 等属性的语法内容请阅读 Flex 布局教程:语法篇 阮一峰

前文曾提到<el-container> 的子元素只能是后四者,后四者的父元素也只能是 <el-container>。因为 只有container 组件指定为 Flex 布局,其余组件若是想要Flex 布局生效,只能将组件作为 container 的子组件,当然 container 可以自包含。

0x04 关注专栏

此文章已收录到专栏中 👇,可以直接关注。

发表评论

相关文章