Расширенный Vue. js Dynami c Функциональный компонент, использующий синтаксис `: is` и функцию рендеринга - PullRequest
1 голос
/ 08 мая 2020

Предпосылки: Я создал стандартный компонент с одним файлом, который принимает name опору и просматривает в разных местах структуру каталогов моего приложения и предоставляет первый сопоставленный компонент с этим именем. Он был создан, чтобы разрешить "дочернюю тематику" в моей Vue. js CMS, которая называется Resto. Это аналогично тому, как WordPress ищет файлы шаблонов, сначала проверяя расположение дочерней темы, а затем возвращаясь к родительской теме, если она не найдена, et c.

Использование : компонент можно использовать следующим образом:

<!-- 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(). Я безуспешно пытался запустить его изнутри функции рендеринга.

Большой вопрос

Как мне найти и динамически импортировать компонент, на который я нацелен, прежде чем передать этот компонент в createElement (или в моем единственном файле <template functional><template/>)?

Спасибо всем вам, Vue -головы! ✌️

Обновление : я наткнулся на это решение для использования функции рендеринга h() и случайной загрузки компонента, но я не уверен, как заставить его работать, чтобы принять name prop ...

...