使用`:is`语法和渲染功能的高级Vue.js动态功能组件

时间:2020-05-07 22:07:43

标签: javascript vue.js dynamic-import child-theming vue-functional-component

背景:我构建了一个标准的单个文件组件,该组件带有一个name属性,并在应用程序的目录结构的不同位置进行查找,并提供了第一个与该名称匹配的组件。创建它是为了在我的Vue.js CMS(称为Resto)中允许“​​子主题化”。这与WordPress查找模板文件的原理类似,首先检查子主题位置,然后如果找不到则将其还原为父主题,等等。

用法:该组件可以这样使用:

<!-- Find the PageHeader component
in the current child theme, parent theme,
or base components folder --->
<theme-component name="PageHeader">
    <h1>Maybe I'm a slot for the page title!</h1>
</theme-component> 

我的目标:我想转换为功能组件,以免影响应用程序的渲染性能或在Vue开发工具中显示。看起来像这样:

<template>
  <component
    :is="dynamicComponent"
    v-if="dynamicComponent"
    v-bind="{ ...$attrs, ...$props }"
    v-on="$listeners"
    @hook:mounted="$emit('mounted')"
  >
    <slot />
  </component>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  name: 'ThemeComponent',
  props: {
    name: {
      type: String,
      required: true,
      default: '',
    },
  },
  data() {
    return {
      dynamicComponent: null,
      resolvedPath: '',
    }
  },
  computed: {
    ...mapGetters('site', ['getThemeName']),
    customThemeLoader() {
      if (!this.name.length) {
        return null
      }
      // console.log(`Trying custom theme component for ${this.customThemePath}`)
      return () => import(`@themes/${this.customThemePath}`)
    },
    defaultThemeLoader() {
      if (!this.name.length) {
        return null
      }
      // console.log(`Trying default component for ${this.name}`)
      return () => import(`@restoBaseTheme/${this.componentPath}`)
    },

    baseComponentLoader() {
      if (!this.name.length) {
        return null
      }
      // console.log(`Trying base component for ${this.name}`)
      return () => import(`@components/Base/${this.name}`)
    },

    componentPath() {
      return `components/${this.name}`
    }, // componentPath

    customThemePath() {
      return `${this.getThemeName}/${this.componentPath}`
    }, // customThemePath()
  },
  mounted() {
    this.customThemeLoader()
      .then(() => {
        // If found in the current custom Theme dir, load from there
        this.dynamicComponent = () => this.customThemeLoader()
        this.resolvedPath = `@themes/${this.customThemePath}`
      })
      .catch(() => {
        this.defaultThemeLoader()
          .then(() => {
            // If found in the default Theme dir, load from there
            this.dynamicComponent = () => this.defaultThemeLoader()
            this.resolvedPath = `@restoBaseTheme/${this.defaultThemePath}`
          })
          .catch(() => {
            this.baseComponentLoader()
              .then(() => {
                // Finally, if it can't be found, try the Base folder
                this.dynamicComponent = () => this.baseComponentLoader()
                this.resolvedPath = `@components/Base/${this.name}`
              })
              .catch(() => {
                // If found in the /components dir, load from there
                this.dynamicComponent = () => import(`@components/${this.name}`)
                this.resolvedPath = `@components/${this.name}`
              })
          })
      })
  },
}
</script>

我已经尝试了许多不同的方法,但是我对功能组件和渲染函数还是比较陌生的。(从未涉足React)。

障碍:我似乎无法弄清楚如何运行在原始mounted()函数中调用的链接函数。我尝试从render函数内部运行它,但没有成功。

大问题

在将组件传递给createElement函数之前(或在我的单个文件<template functional><template/>中),如何找到并动态导入要定位的组件?

感谢所有Vue-heads! ✌️

更新 :我偶然发现this solution使用了h()渲染功能并随机加载了一个组件,但是我不确定如何使它能够接受name道具...

0 个答案:

没有答案