Element-UI 技術揭祕(4)— Container 佈局容器組件的設計與實現。

前言

上一篇文章咱們分析了 Layout 佈局組件的設計和實現,它的應用場景一般是局部佈局。對於整個頁面的佈局,element-ui 提供了 Container 佈局容器組件,專門用於 PC 管理後臺頁面的總體佈局。javascript

需求分析

咱們先經過幾幅圖看一下頁面的常見佈局。css

這兩張圖的佈局在後臺系統中很常見,經過簡單的 CSS 就能夠實現。不過咱們更喜歡用組件化的開發方式,把這些 CSS 的細節封裝到組件裏,以下:html

<el-container>
  <el-header>Header</el-header>
  <el-main>Main</el-main>
  <el-footer>Footer</el-footer>
</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>
複製代碼

咱們使用了 <el-container><el-header><el-main><el-footer><el-aside> 5 種組件,來看一下它們各自的做用:前端

  • <el-container>:外層容器。當子元素中包含 <el-header><el-footer> 時,所有子元素會垂直上下排列,不然會水平左右排列。java

  • <el-header>:頂欄容器。node

  • <el-aside>:側邊欄容器。element-ui

  • <el-main>:主要區域容器。sass

  • <el-footer>:底欄容器。ide

這幾個組件須要遵循以下約束:組件化

<el-container> 的子元素只能是後四者,後四者的父元素也只能是 <el-container>

瞭解了 element-ui Container 佈局容器組件的需求後,咱們來分析它的設計和實現。​

設計和實現

針對圖示的佈局,咱們能夠經過 flex 的佈局方式輕鬆實現,element-ui 也是基於 flex 佈局實現的,接下來咱們來分析各個組件的實現。

ElContainer 組件

先來看模板部分:

<template>
  <section class="el-container">
    <slot></slot>
  </section>
</template>
複製代碼

<el-container> 組件會渲染成一個 <section> 標籤,並經過 slot 作內容分發。

再來看一下 CSS 部分:

@include b(container) {
  display: flex;
  flex-direction: row;
  flex: 1;
  flex-basis: auto;
  box-sizing: border-box;
  min-width: 0;
}
複製代碼

重點看一下 CSS 部分,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 表示分配空間以前會先跟父容器預定自身內容大小的空間,而後剩下的纔會納入到剩餘空間。

<el-container> 容器默認是水平排列,固然也須要支持垂直排列,咱們能夠給組件提供一個 directionprop,若是傳入的 directionvertical,添加對應的 CSS。

<template>
  <section class="el-container" :class="{ 'is-vertical': isVertical }">
    <slot></slot>
  </section>
</template>
複製代碼
export default {
  name: 'ElContainer',

  componentName: 'ElContainer',

  props: {
    direction: String
  },

  computed: {
    isVertical() {
      if (this.direction === 'vertical') {
        return true;
      } else if (this.direction === 'horizontal') {
        return false;
      }
    }
  }
};
複製代碼
@include when(vertical) {
  flex-direction: column;
}
複製代碼

若是傳入的 directionvertical,則添加 is-vertical 的 CSS,最終經過修改 flex-direction:column 實現內部元素是在垂直方向排列。

回顧前面的一個需求:當 <el-container> 容器的子元素中包含 <el-header><el-footer> 時,所有子元素會垂直上下排列,不然會水平左右排列。

實現它也很容易,擴展計算屬性 isVertical 的判斷條件便可:

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; 
  }
}
複製代碼

這裏用了一個小技巧,this.$slots.default 獲取的是默認插槽中的全部 vnodes 節點,而後對他們遍歷,經過 vnode.componentOptions.tag 來判斷這個 vnode 是否是 <el-header> 或者是 <el-footer>vnode.componentOptions 並不在官網 API 裏,可是對於熟讀 Vue 源碼的人來講並不陌生。

ElHeader 組件

先來看一下模板部分:

<template>
  <header class="el-header">
    <slot></slot>
  </header>
</template>
複製代碼

<el-header> 組件會渲染成一個 <header> 標籤,並經過 slot 作內容分發。

再來看一下 CSS 部分:

@include b(header) {
  padding: $--header-padding;
  box-sizing: border-box;
  flex-shrink: 0;
}
複製代碼

其中 $--header-padding 是一個變量,在 packages/theme-chalk/src/common/var.scss 文件中定義。flex-shrink: 0 表示即便空間不夠,也不會縮小 <el-header> 所佔空間。

一般來講頭部都會有一個固定高度,所以 <el-header> 容許你傳入一個 heightprops 來指定高度,若是不指定的話提供一個默認高度。

<template>
  <header class="el-header" :style="{ height }">
    <slot></slot>
  </header>
</template>
複製代碼
export default {
  name: 'ElHeader',

  componentName: 'ElHeader',

  props: {
    height: {
      type: String,
      default: '60px'
    }
  }
};
複製代碼

因爲直接經過 :style 設置的樣式,因此這裏傳入高度的時候必定要攜帶單位。

ElMain 組件

先來看一下模板部分:

<template>
  <main class="el-main">
    <slot></slot>
  </main>
</template>
複製代碼

<el-main> 組件會渲染成一個 <main> 標籤,並經過 slot 作內容分發。

再來看一下 CSS 部分:

@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;
}
複製代碼

注意,<main> 標籤在 IE11 中是部分支持的。一般 <main> 中包裹的內容徹底由它的子元素來決定,因此並不會設置高和寬,只是經過 flex:1 來分配 <el-container> 容器的剩餘空間。

ElFooter 組件

先來看一下模板部分:

<template>
  <footer class="el-footer">
    <slot></slot>
  </footer>
</template>
複製代碼

<el-footer> 組件會渲染成一個 <footer> 標籤,並經過 slot 作內容分發。

再來看一下 CSS 部分:

@include b(footer) {
  padding: $--footer-padding;
  box-sizing: border-box;
  flex-shrink: 0;
}
複製代碼

和頭部同樣,一般底部也會有一個固定高度,所以 <el-footer> 容許你傳入一個 heightprops 來指定高度,若是不指定的話提供一個默認高度。

<template>
  <footer class="el-footer" :style="{ height }">
    <slot></slot>
  </footer>
</template>
複製代碼
export default {
  name: 'ElFooter',

  componentName: 'ElFooter',

  props: {
    height: {
      type: String,
      default: '60px'
    }
  }
};
複製代碼

ElAside 組件

先來看一下模板部分:

<template>
  <aside class="el-aside">
    <slot></slot>
  </aside>
</template>
複製代碼

<el-aside> 組件會渲染成一個 <aside> 標籤,並經過 slot 作內容分發。

再來看一下 CSS 部分:

@include b(aside) {
  overflow: auto;
  box-sizing: border-box;
  flex-shrink: 0;
}
複製代碼

<el-aside> 組件用來渲染側邊欄,而側邊欄一般會有寬度,所以 <el-aside>,容許你傳入一個 widthprops 來指定寬度,若是不指定的話提供一個默認寬度。

<template>
  <aside class="el-aside" :style="{ width }">
    <slot></slot>
  </aside>
</template>
複製代碼
export default {
  name: 'ElAside',

  componentName: 'ElAside',

  props: {
    width: {
      type: String,
      default: '300px'
    }
  }
};
複製代碼

總結

element-uiContainer 佈局容器組件的實現仍是很簡單的:建立了一些語義化的標籤,利用插槽作內容分發,經過 flex 實現佈局效果。

學習完這篇文章,你能夠順便對 flex 佈局、HTML5 的語義化標籤作一下複習,加深理解,並瞭解到 Vue 源碼中的一些小技巧。

把不會的東西學會了,那麼你就進步了,若是你以爲這類文章有幫助,也歡迎把它推薦給你身邊的小夥伴。

下一篇預告 :Element-UI 技術揭祕(5)色彩、字體、邊框與圖標。

另外,我最近剛開了公衆號「老黃的前端私房菜」,《Element-UI 技術揭祕》系列文章會第一時間在公衆號更新和發佈,除此以外,我還會常常分享一些前端進階知識,乾貨,也會偶爾分享一些軟素質技能,歡迎你們關注喔~

相關文章
相關標籤/搜索