Element-UI Container 布局容器源码
用于布局的容器组件,方便快速搭建页面的基本结构:
<el-container>
:外层容器。当子元素中包含 <el-header>
或 <el-footer>
时,全部子元素会垂直上下排列,否则会水平左右排列。
<el-header>
:顶栏容器。
<el-aside>
:侧边栏容器。
<el-main>
:主要区域容器。
<el-footer>
:底栏容器。
以上组件采用了 flex 布局,使用前请确定目标浏览器是否兼容。此外,
<el-container>
的子元素只能是后四者,后四者的父元素也只能是<el-container>
。
ElContainer
组件
模板:
<template>
//is-vertical 判断是是否为垂直布局
//section 利于`SEO`的语义化`tag`标签
<section class="el-container" :class="{ 'is-vertical': isVertical }">
<slot></slot> //通过内容分发
</section>
</template>
<script>
export default {
name: 'ElContainer',
componentName: 'ElContainer', //自定义属性
props: {
direction: String //方向 horizontal =》 水平 vertical =》垂直
},
computed: {
isVertical() {
if (this.direction === 'vertical') {
return true;
} else if (this.direction === 'horizontal') {
return false;
}
//this.$slots.default是个数组,里面的每个元素都是一个VNode,VNode是虚拟dom中的虚拟节点,当组件被编译时,每个<...>就会生成一个虚拟的节点,
//通过 vnode.componentOptions.tag 来判断这个 vnode 是不是 <el-header> 或者是 <el-footer>。
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;
}
}
};
</script>
首先判断
this.$slots&& this.$slots.default
【this.$slots.default
为默认插槽】,如果不存在直接返回false,不存在的情况就是子元素为空。this.$slots
是组件的实例属性,组件是可复用的Vue的实例,和 new Vue()一样是实例,因此有以下属性
CSS
部分:
@include b(container) {
display: flex;
flex-direction: row;
flex: 1;
flex-basis: auto;
box-sizing: border-box;
min-width: 0;
}
@include when(vertical) { //当是垂直方向排列,则设置为 column
flex-direction: column;
}
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
表示分配空间之前会先跟父容器预约自身内容大小的空间,然后剩下的才会归入到剩余空间。
ElHeader
组件
模板:
<template>
//header 便于 SEO
<header class="el-header" :style="{ height }">
// height 这里传入的 height 是需要带单位的
<slot></slot>
</header>
</template>
<script>
export default {
name: 'ElHeader',
componentName: 'ElHeader',
props: { //通过属性来控制 header 高度
height: {
type: String,
default: '60px'
}
}
};
</script>
CSS
部分
@include b(header) {
padding: $--header-padding; //packages/theme-chalk/src/common/var.scss scss变量
box-sizing: border-box;
flex-shrink: 0; //表示即使空间不够,也不会缩小 <el-header> 所占空间。
}
ElMain
组件
模板:
<template>
// main SEO
<main class="el-main">
<slot></slot>
</main>
</template>
<script>
export default {
name: 'ElMain',
componentName: 'ElMain'
};
</script>
CSS
部分:
@include b(main) {
// IE11 supports the <main> element partially https://caniuse.com/#search=main
// IE 支持11 以上
display: block;
flex: 1;
flex-basis: auto;
overflow: auto;
box-sizing: border-box;
padding: $--main-padding;
}
通常
<main>
中包裹的内容完全由它的子元素来决定,所以并不会设置高和宽,只是通过flex:1
来分配<el-container>
容器的剩余空间。
ElFooter
组件
模板:
<template>
// footer SEO
<footer class="el-footer" :style="{ height }">
<slot></slot>
</footer>
</template>
<script>
export default {
name: 'ElFooter',
componentName: 'ElFooter',
props: { //属性传值 控制高度
height: {
type: String,
default: '60px'
}
}
};
</script>
CSS
部分:
@include b(footer) {
padding: $--footer-padding;
box-sizing: border-box;
flex-shrink: 0;
}
flex-shrink: 0; 表示即使空间不够,也不会缩小 所占空间。
ElAside
组件
模板:
<template>
//aside SEO
<aside class="el-aside" :style="{ width }">
<slot></slot>
</aside>
</template>
<script>
export default {
name: 'ElAside',
componentName: 'ElAside',
props: { //属性传值
width: {
type: String,
default: '300px'
}
}
};
</script>
CSS
部分:
@include b(aside) {
overflow: auto; // 如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。
box-sizing: border-box;
flex-shrink: 0; //表示即使空间不够,也不会缩小 <el-aside> 所占空间。
}
总结
element-ui
的 Container
布局容器组件的实现还是很简单的:创建了一些语义化的标签,利用插槽做内容分发,通过 flex
实现布局效果。
其中header
、footer
、 aside
的flex-shrink
都是0,也就是不会压缩,而main
中的代码flex:1
表示了只有main会被压缩或者扩张,flex:1
是flex:grow:1,flex:shrink:1,flex-basis:auto
的简写,又因为flex:grow
默认值为0,所以只有main
会被压缩或扩张,header
、footer
、 aside
都不变
整个布局其实就是对原生的封装,主要就是container
的处理,如果浏览器不支持flex
,则上述布局无效