elementui自定义组件开发(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>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 https://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 关注专栏
此文章已收录到专栏中 ? ,可以直接关注 。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!