Vue动态组件渲染

Yukino 360 2021-12-29

前段时间给项目的无代码系统做了一个交互预览页面,主要功能是把UI信息的JSON解析渲染为可交互的网页,其中遇到了不同类型组件渲染的问题

一、为什么需要动态组件渲染

我们的无代码系统采用PCB协议(Page、Container和Block),为了实现交互,Container分为有焦点和无焦点,Block作为最基础的页面元素,其种类包含图片、文本、视频等16种,如果又要保证页面结构简单明晰,又要让Block有较高的灵活实现的话,就避不开是用动态组件。

二、动态组件的实现方式

对于上面所说的情况,实际中针对不同的场景采取了两种不同的实现方式。

1. JSX结合if判断实现

这种方式是用于解决Container的两种不同组件,即有焦点和无焦点,由于这两个组件有较大的功能差异,分成了单独的两个组件实现,同时在props上也有一定区别,上述情况综合考虑,使用了JSX结合if判断进行条件渲染实现,大致实现如下:

<script>
import FocusContainer from "./focus-container.vue";
import NoFocusContainer from "./no-focus-container.vue";
export default {
  name: "Container",
  components: {
    FocusContainer,
    NoFocusContainer
  },
  render() {
    const { hasfocus } = this.uiData;
    return hasfocus ? (
      <FocusContainer
        uiData={this.uiData}
        isActive={this.isActive}
     ></FocusContainer>
    ) : (
      <NoFocusContainer
       uiData={this.uiData}
      ></NoFocusContainer>
    );
  }
};
</script>

总的来说,这种适合组件总数数量少、props有差异、没有后续改变或频繁改变的场景。

2. Component is动态组件实现

第一种方式对于只有两个类型的Container还相对容易,但是对于实际中类型的Block要是写起if…else或者switch那也是相当难维护,所以第二种方式是用Vue官方提供的is属性实现动态组件,没有印象的可以先看看组件基础 — Vue.js动态组件 & 异步组件 — Vue.js,大致实现如下:

<template>
  <component
    :is="blockType"
    :uiData="uiData"
 ></component>
</template>

<script>
import BlockCommonPropsMixin from "./block-common-props-mixin";
import ImageBlock from "./image/image-block.vue";
import TextBlock from "./text/text-block.vue";
import {
  TEXT_BLOCK,
  IMAGE_BLOCK
} from "./block-type";
const componentMap = {
  [TEXT_BLOCK]: TextBlock,
  [IMAGE_BLOCK]: ImageBlock,
};

export default {
  name: "Block",
  mixins: [BlockCommonPropsMixin],
  computed: {
    blockType() {
      const { type } = this.uiData;
      return componentMap[type];
    }
  }
};
</script>

先将类型和组件对象存储在一个Map中,然后声明一个计算属性返回对应的组件对象,将这个作为componentis属性的值即可,这里有几点注意的:
1. 将Block的类型放在一个单独的文件中,方便后续的维护;
2. Block和其他类型的Block的props可以提取成一个mixin文件;
3. 如果后续(组件类型)有变化,可以使用keep-alive包裹实现缓存。
总的来说,这种方式适用于组件数量多、组件props基本一致的情况。


# 前端 # Vue