6.1Element UI布局容器
Element UI Container 与 Layout 布局详解
一、Container 布局容器
Container 布局容器用于整体页面布局,由以下组件构成,需遵循父子关系约束:
<el-container>
:外层容器,子元素只能是<el-header>
、<el-aside>
、<el-main>
、<el-footer>
<el-header>
:顶栏容器<el-aside>
:侧边栏容器<el-main>
:主要区域容器<el-footer>
:底栏容器
布局规则:
- 当子元素包含
<el-header>
或<el-footer>
时,垂直排列 - 否则水平排列(默认 flex 布局)
- 需设置容器高度(通常为
100vh
)以确保正常显示
示例代码:
<el-container style="height: 100vh"><el-aside width="200px">侧边栏</el-aside><el-container><el-header>顶部栏</el-header><el-main>主要内容区</el-main><el-footer>底部栏</el-footer></el-container>
</el-container>
1. <el-container>
源码分析
<el-container>
是整个布局系统的根容器,它控制着子组件的排列方向
// 文件: packages/layout/src/container.vue
export default {name: 'ElContainer', // 组件名称// 接收一个可选的 'direction' propprops: {direction: {type: String,validator(val) {// 只接受 'horizontal' 或 'vertical'return ['horizontal', 'vertical'].indexOf(val) !== -1;}}},// 计算属性,用于动态生成 CSS 类名computed: {// 根据 'direction' prop 和子组件类型,确定排列方向// 如果没有指定 direction,则根据子组件中是否存在 <el-header> 或 <el-footer> 来推断// 如果有 <el-header> 或 <el-footer>,默认为垂直排列 (column)// 如果只有 <el-aside> 和 <el-main>,默认为水平排列 (row)// 这个推断逻辑通常在 mounted 或 render 函数中处理,这里简化为 computedclassObj() {if (this.direction === 'horizontal') {return 'is-horizontal';} else if (this.direction === 'vertical') {return 'is-vertical';} else {// 自动推断逻辑 (简化版)// 实际源码中会检查 $slots.default 中的子组件标签// const hasHeaderOrFooter = this.$slots.default.some(slot =>// slot.componentOptions && ['el-header', 'el-footer'].includes(slot.componentOptions.tag)// );// return hasHeaderOrFooter ? 'is-vertical' : 'is-horizontal';return 'auto-direction'; // 代表自动推断}}},// 渲染函数render(h) {// 渲染一个 div,应用基础类名 'el-container' 和计算出的方向类名return h('section', { // 注意:实际源码可能用 'div',但为了语义化有时用 'section'class: ['el-container', this.classObj]}, this.$slots.default); // 插槽内容,即其子组件 (<el-header>, <el-aside> 等)}
};
关键点:
direction
Prop: 允许显式指定布局方向 (horizontal
或vertical
)。- 自动推断: 如果没有设置
direction
,组件会根据其子组件的内容自动推断方向。如果包含<el-header>
或<el-footer>
,则推断为垂直 (column
);如果只包含<el-aside>
和<el-main>
,则推断为水平 (row
)。这是实现多种布局模式(如Header + Main
vsAside + Main
)的关键。 - 渲染: 最终渲染为一个
<section>
或<div>
,并带有el-container
和方向类名(如is-vertical
)。
2. <el-header>
、<el-aside>
、<el-main>
、<el-footer>
源码分析
这四个组件的源码结构极其相似,它们的主要区别在于默认的 CSS 样式和可接受的 prop
。
// 通用模板 (以 <el-header> 为例,其他类似)
// 文件: packages/layout/src/header.vue (其他组件在对应文件)
export default {name: 'ElHeader', // 或 'ElAside', 'ElMain', 'ElFooter'props: {// 只有 <el-aside> 和 <el-header> 有 height/width prop// <el-main> 和 <el-footer> 通常没有,它们是自适应的height: { // <el-header> 和 <el-footer> 有 heighttype: String,default: '60px' // Element UI 默认高度},width: { // <el-aside> 有 widthtype: String,default: '300px' // Element UI 默认宽度 (注意:实际默认值可能在 CSS 或这里定义)}// <el-main> 和 <el-footer> 通常没有特定的 prop},render(h) {// 渲染一个 div,应用基础类名和组件特定的类名// 对于 <el-aside>,还会应用 :style 来设置 widthconst tag = this.$attrs.tag || 'header'; // 可以通过 tag prop 自定义标签const data = {class: ['el-header'], // 类名根据组件不同而变化: el-header, el-aside, el-main, el-footerstyle: {}};// 只有 <el-aside> 需要设置 width 样式// 只有 <el-header> 和 <el-footer> 需要设置 height 样式if (this.$options.name === 'ElAside' && this.width) {data.style.width = this.width;} else if (['ElHeader', 'ElFooter'].includes(this.$options.name) && this.height) {data.style.height = this.height;}return h(tag, data, this.$slots.default);}
};
关键点:
- 简单渲染: 它们几乎不做任何逻辑处理,主要就是渲染一个带有特定类名的元素。
- Props:
<el-aside>
:接受width
(String),用于设置侧边栏宽度。<el-header>
/<el-footer>
:接受height
(String),用于设置顶栏/底栏高度。<el-main>
:不接受height
或width
,它的尺寸由父容器和兄弟组件决定(通常是自适应填充剩余空间)。
tag
Prop: 所有组件都支持tag
prop (通过this.$attrs
透传),允许你自定义渲染的 HTML 标签(如将<el-header>
渲染为<header>
或<div>
)。
CSS 样式分析 (核心)
CSS 是实现布局行为的关键。以下是核心 CSS 规则的解读:
/* 基础容器样式 */
.el-container {display: flex;/* 默认是垂直方向,但会被 is-horizontal 覆盖 */flex-direction: column;/* 占据父容器的全部可用高度 *//* 注意:为了让 el-container 占满视口,通常需要设置 html, body, #app 的 height: 100%; */box-sizing: border-box;/* 其他基础样式 */
}/* 水平布局:当 direction="horizontal" 或自动推断为水平时添加 */
.el-container.is-horizontal,
.el-container.auto-direction /* 代表推断为水平 */ {flex-direction: row;
}/* 顶栏、底栏、侧边栏、主内容区的基础样式 */
.el-header,
.el-footer {/* 固定高度由 prop 或 CSS 默认值决定 *//* flex: 0 0 auto; 这是默认值,表示不伸缩,基于内容或设置的高度 */
}.el-aside {/* 固定宽度由 prop 决定 *//* flex: 0 0 auto; 不伸缩 */
}.el-main {/* 关键!主内容区会伸缩以填充剩余空间 */flex: 1;/* 占据除了固定高度/宽度组件之外的所有剩余空间 *//* min-width: 0; 可能需要设置,防止内容过长时 el-aside 被挤压 *//* 其他样式如 padding */
}/* 当容器是垂直排列时,.el-aside 和 .el-main 的组合 */
.el-container.is-vertical > .el-aside,
.el-container.auto-direction > .el-aside /* 推断为垂直时的 aside */ {/* 在垂直主轴上,aside 作为子项,需要设置其内部的 flex-direction 为 row *//* 这允许 aside 内部的 main 和其他元素水平排列 *//* 实际 CSS 可能更复杂,可能需要嵌套容器 *//* 但核心思想是,一个 vertical 的 container 包含一个 horizontal 的 container */
}/* 可能存在的嵌套规则 */
.el-container.is-vertical > .el-container {/* 内层容器在垂直主轴上 */flex-direction: row; /* 通常内层容器用于 Aside + Main,所以是 row */flex: 1; /* 内层容器也伸缩 */
}
CSS 核心要点:
display: flex;
:<el-container>
是一个 Flex 容器。flex-direction
: 由is-horizontal
或is-vertical
(或自动推断) 类名控制主轴方向。flex: 1;
on<el-main>
: 这是<el-main>
能够自适应填充剩余空间的核心。flex: 1
是flex-grow: 1
,flex-shrink: 1
,flex-basis: 0%
的简写,意味着它会尽可能地伸展来占据可用空间。- 固定尺寸:
<el-header>
、<el-footer>
的height
和<el-aside>
的width
通过style
属性(由prop
设置)或 CSS 默认值来固定。 - 嵌套实现复杂布局: 像
Header + Aside + Main
这样的布局,实际上是<el-container>
(垂直) -><el-header>
+<el-container>
(水平) -><el-aside>
+<el-main>
。内层的<el-container>
负责水平排列侧边栏和主内容。
Layout 栅格布局
基于 24 栏等宽划分,通过 <el-row>
和 <el-col>
组件实现灵活布局:
el-row
(行):- 相当于一个容器,用来包裹一组
el-col
(列)。 - 它本身是一个
display: flex
的容器。 - 通过设置
el-row
的属性,可以控制其内部所有el-col
的水平对齐 (justify
)、垂直对齐 (align
) 和列间距 (gutter
)。
- 相当于一个容器,用来包裹一组
el-col
(列):- 代表布局中的一列。
- 通过
span
属性来指定该列占据的份数。Element UI 的栅格系统默认将一行分为 24 份。 - 例如:
<el-col :span="12">
表示该列占据一半(12/24)的宽度;<el-col :span="6">
表示占据四分之一(6/24)的宽度。
核心属性:
分栏设置:通过
span
属性定义占据列数(1-24)<el-row><el-col :span="8">占8列</el-col><el-col :span="16">占16列</el-col> </el-row>
间隔控制:
gutter
属性设置列间距(像素值)<el-row :gutter="20"><el-col :span="12"></el-col><el-col :span="12"></el-col> </el-row>
偏移设置:
offset
属性控制列偏移<el-col :span="6" :offset="6">向右偏移6列</el-col>
对齐方式:设置
type="flex"
启用 Flex 布局,通过justify
控制水平对齐<el-row type="flex" justify="space-between"><el-col :span="6"></el-col><el-col :span="6"></el-col> </el-row>
注意事项:
- Container 组件需保持严格的父子组件关系
- Layout 栅格支持响应式断点(如
:xs="8" :sm="12"
) - 复杂布局可组合使用两种方案(Container 作为整体框架,内部嵌套 Layout 栅格)