4.1 简单轮子 VueContainer 布局组件

[toc]

需求分析

  • 实现常用布局
    • 三栏布局
      • 中栏 左侧菜单栏
      • 中栏 右侧菜单栏
    • 左右两栏布局 左侧菜单栏

API设计

  • VueMain.vue
  • VueContainer.vue
    • width
  • VueHeader.vue
    • height
  • VueHeader.vue
  • VueFooter.vue
  • VueAside.vue
    • width

组件UI结构

  • 可嵌套的<v-layout>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!-- 三栏布局 -->
<v-layout>
  <v-header></v-header>
  <v-content></v-content>
  <v-footer></v-footer>
</v-layout>

<!-- 三栏布局 中栏 左侧菜单栏 -->
<v-layout>
  <v-header></v-header>
  <v-layout>
    <v-sider></v-sider>
    <v-content></v-content>
  </v-layout>
  <v-footer></v-footer>
</v-layout>

<!-- 三栏布局 中栏 右侧菜单栏-->
<v-layout>
  <v-header></v-header>
  <v-layout>
    <v-content></v-content>
    <v-sider></v-sider>
  </v-layout>
  <v-footer></v-footer>
</v-layout>

<!-- 左右两栏布局 左侧菜单栏 -->
<v-layout>
  <v-sider></v-sider>
  <v-layout>
    <v-header></v-header>
    <v-content></v-content>
  </v-layout>
  <v-footer></v-footer>
</v-layout>
  • 重复使用<v-layout>作为嵌套容器,相当于div标签

组件代码

  • 分别创建组件
    • VueContainer.vue
    • VueMain.vue
    • VueHeader.vue
    • VueAside.vue
    • VueFooter.vue
  • 全局注册组件
    • Vue.component('v-container', VueContainer)
    • Vue.component('v-main', VueMain)
    • Vue.component('v-header', VueHeader)
    • Vue.component('v-asider', VueAside)
    • Vue.component('v-footer', VueFooter)
  • IDE 自动导入import VueContainer from ...

./src/components/layout/VueContainer.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
  <section class="container">
    <slot></slot>
  </section>
</template>

<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';

@Component
export default class VueContainer extends Vue {
  name = 'VueContainer';
}
</script>

<style lang="scss" scoped>
.container {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
}
</style>

  • 其他组件结构类似

LayoutGroups.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<template>
  <div>
    <details open>
      <summary>三栏布局</summary>
      <vue-main style="height: 20vh;">
        <vue-header class="demo">Header</vue-header>
        <vue-container class="demo">Content</vue-container>
        <vue-footer class="demo">Footer</vue-footer>
      </vue-main>
    </details>
    <br>
    <details open>
      <summary>三栏布局 中栏 左侧菜单栏</summary>
      <vue-main style="height: 20vh;">
        <vue-header class="demo">Header</vue-header>
        <vue-container class="demo">
          <vue-aside>Aside</vue-aside>
          <vue-container>Content</vue-container>
        </vue-container>
        <vue-footer class="demo">Footer</vue-footer>
      </vue-main>
    </details>
    <br>
  </div>
</template>

<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
import VueMain from './layout/VueMain.vue';
import VueContainer from './layout/VueContainer.vue';
import VueAside from './layout/VueAside.vue';
import VueHeader from './layout/VueHeader.vue';
import VueFooter from './layout/VueFooter.vue';

@Component({
  components: {
    VueMain,
    VueContainer,
    VueAside,
    VueHeader,
    VueFooter
  }
})
export default class extends Vue {
  name = 'LayoutGroups';
}
</script>

<style lang="scss" scoped>
@import '../style/global.scss';
.demo {
  border: 1px solid orange;
  min-height: 40px;
}
</style>

父子组件嵌套时创建和挂载的顺序

父子组件嵌套时,父组件视图和子组件视图在页面中渲染谁先谁后?

  • 不确定
  • 子组件先于父组件mounted
  • 所有子孙组件都mounted后,根(父)组件最后mounted到页面中

测试的顺序

  • 父组件beforeCreated
  • 父组件created
    • 子组件beforeCreated
    • 子组件created
      • 孙组件…递归查找
    • 子组件mounted
  • 父组件mounted

父子组件嵌套时,所有组件视图都渲染完成后再执行回调操作

1
2
3
4
5
6
7
...
mounted() {
    this.$nextTick(()=>{
        // 仅在渲染整个视图之后,再立即运行的代码
    })
}
...

组件name属性的作用

  • 使用浏览器vue插件时可以直接看到组件原来的命名,而不是直接显示HTML标签名
  • 组件的实例中的name属性
    • 可以通过判断是否存在特定的name,从而来实现相应的逻辑

打印出VueContainer.vue中,所有子组件的name属性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<template>
  <section class="container">
    <slot></slot>
  </section>
</template>

<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';

@Component
export default class VueContainer extends Vue {
  name = 'VueContainer';
  mounted() {
    this.$children.forEach((vm)=> {
      console.log(vm.$options.name);
    })
  }
}
</script>

实现判断子组件中有VueAside,方向变为row

  • 获取子组件的集合(数组) this.$children

VueContainer.vue


单元测试