vue3 进阶: 动态组件实战

1,682 阅读1分钟

一、需求场景

我们日常工作中遇到一种列表需要同时展示多种不同 cell 视图的场景(常见聊天列表和消息列表)。大部分人第一直觉就是 if 指令,这种方式虽然能实现,但是当样式超过两种,代码就会异常丑陋和不利于维护。实现这种需求最优雅的实现方式是动态组件实现:通过信息类型返回对应的组件,有些像 templet 中的工厂模式;

二、效果图(假设三种消息类型)

DynamicCompAdvanced.png

三、核心代码

//src/views/DynamicCompAdvanced.vue

<template>
  <div class="page_container">
    <navbar
      isleftarrow
      navBarTitle="动态组件进阶" 
      closeWebview
    >
    </navbar> 
    <component 
      v-for="(item, index) in items" 
      :key="index"
      :is="currentComp(item.type)"//返回组件
      :obj="item"//正常组件传值此处传值为对象typeMap中有所有组件必须有 obj 属性)
    >
    </component> 
    </div> 
</template>

<script setup>
import navbar from '@/components/navbar.vue';
import DynamicCellOne from '@/components/DynamicCellOne.vue'
import DynamicCellTwo from '@/components/DynamicCellTwo.vue'
import DynamicCellThree from '@/components/DynamicCellThree.vue'

import {ref, reactive, computed} from "vue";

const items = reactive([
  {
    title: '热门活动',
    content: '您收到一条热门活动',
    icon: require('@/assets/images/icon_notice_popular.png'),
    imgUrl: 'https://ys-oss-zjrs.haier.net/content/img/2022081016074732563876.jpg',
    type: 5,
    eventID: 'BD10030',
    index: 0,
    time: "2022-08-10 09:15",
  },
  {
    title: '系统通知',
    content: '您收到一条系统通知',
    icon: require('@/assets/images/icon_notice_system.png'),
    type: 0,
    imgUrl: 'https://ys-oss-zjrs.haier.net/content/img/2022081016074732563876.jpg',
    eventID:'BD10031',
    index: 1,
    time: "2022-08-11 11:15",
  },
  {
    title: '进度消息',
    content: '您收到一条进度消息',
    icon: require('@/assets/images/icon_notice_schedule.png'),
    imgUrl: 'https://ys-oss-zjrs.haier.net/content/img/2022081016074732563876.jpg',
    type: 6,
    eventID: 'BD10032',
    index: 2,
    time: "2022-08-12 19:15",
  },
]);

// 所有类型和组件映射关系
const typeMap = {
  5: DynamicCellOne,
  0: DynamicCellTwo,
  6: DynamicCellThree,
};

// 当前组件
const currentComp = (type) => {
  const key = type.toString();
  if (Object.keys(typeMap).includes(key)) {
    return typeMap[key];
  }
  return typeMap[0];
};

</script>


<style scoped lang="scss">
.page_container {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;

  background-color: #f6f6f6;
}
</style>

总结

若是一个列表中需要展示多种(大于一种),都应该用动态组件实现,这样无论从代码量、维护性和扩展性上,都是最好的选择。

DynamicCompAdvanced.vue

DynamicCellOne.vue

DynamicCellTwo.vue

DynamicCellThree.vue