Chargement dynamique de composants Vue avec import.meta.glob et rendu conditionnel via component

Dans Vue, import.meta.glob est une fonctionnalité fournie par Vite pour importer dynamiquement des ficheirs. L'option eager: true permet de charger les fichiers de manière synchrone, au lieu du chargemant asynchrone par défaut.

Par défaut, import.meta.glob retourne un objet contenant des chemins de modules et des fonctions d'importation asynchrone, chargeant les modules à la demande (chargement paresseux). En définissant { eager: true }, Vite charge immédiatement tous les fichiers correspondants lors de la construction et les inclut directement dans l'objet résultat en tant que modules résolus. Cela signifie que le contenu des fichiers est disponible instantanément, sans requêtes asynchrones supplémentaires.

const importedModules = import.meta.glob('./components/*.vue', { eager: true });

Format de sortie avec eager: true : Chaque module a une propriété default contenant directement l'objet composant Vue.

{
  './components/WidgetA.vue': { default: VueComponent },
  './components/WidgetB.vue': { default: VueComponent },
  // ...
}

Sans eager (défaut, eager: false) : Chaque module est une fonction retournant une Promesse, nécessitant un appel manuel pour charger le module.

{
  './components/WidgetA.vue': () => import('./components/WidgetA.vue'),
  './components/WidgetB.vue': () => import('./components/WidgetB.vue'),
  // ...
}

Exemple avec eager: true

<template>
  <div>
    <!-- Composant dynamique, rendu basé sur la valeur de selectedWidget -->
    <component
      ref="widgetRef"
      :is="activeComponent"
    />
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';

const props = defineProps(['widgetName']);
const widgetRef = ref(null);
const availableWidgets = {};

const widgetFiles = import.meta.glob('./components/*.vue', { eager: true });

for (const filePath in widgetFiles) {
  const fileModule = widgetFiles[filePath];
  const widgetId = filePath.split('/').pop()?.replace('.vue', '');
  availableWidgets[widgetId] = fileModule.default;
}

const activeComponent = computed(() => {
  const requestedName = props.widgetName;
  if (requestedName && availableWidgets[requestedName]) {
    return availableWidgets[requestedName];
  }
  return null;
});

defineExpose({});
</script>

<style lang="scss" scoped></style>

Exemple avec eager: false

<template>
  <div>
    <component
      ref="widgetRef"
      :is="activeComponent"
      v-if="activeComponent"
    />
  </div>
</template>

<script setup>
import { ref, computed, shallowRef } from 'vue';

const props = defineProps(['widgetName']);
const widgetRef = ref(null);
const loadedWidgets = ref({});
const widgetFiles = import.meta.glob('./components/*.vue', { eager: false });
const currentWidget = shallowRef(null);

const loadWidget = async (name) => {
  if (loadedWidgets.value[name]) {
    return loadedWidgets.value[name];
  }
  const matchingPath = Object.keys(widgetFiles).find((key) =>
    key.split('/').pop().replace('.vue', '') === name
  );
  if (matchingPath && widgetFiles[matchingPath]) {
    const module = await widgetFiles[matchingPath]();
    loadedWidgets.value[name] = module.default;
    return module.default;
  }
  return null;
};

const activeComponent = computed(() => {
  const name = props.widgetName;
  if (name) {
    if (loadedWidgets.value[name]) {
      return loadedWidgets.value[name];
    }
    loadWidget(name).then((component) => {
      currentWidget.value = component || null;
    });
  }
  return currentWidget.value;
});

defineExpose({});
</script>

<style lang="scss" scoped></style>

Raisons d'utiliser eager: true

Dans ce contexte, les composants de widgets sont limités en nombre et doivent être disponibles immédiatement au chargement de la page, évitant les délais de chargement asynchrone. Avec eager: false, un appel manuel via widgetFiles[path]() est requis pour charger les composants de manière asynchrone, ce qui peut entraîner un état de chargement temporaire lors du changement, impactant l'expérience utilisateur.

Considérations importantes

  • Performance : eager: true inclut tous les composants correspondants dans le bundle initial. Si le nombre de composants est élevé, cela peut augmenter le temps de chargement initial. Il faut évaluer si eager: false pour un chargement à la demande est plus approprié.
  • Chemins : Assurez-vous que le pattern ./components/*.vue corerspond correctement aux fichiers cibles ; le chemin est relatif au fichier actuel.
  • Noms des composants : La prop widgetName doit correspondre au nom du fichier (sans l'extension .vue), par exemple 'WidgetA'.
  • Spécifique à Vite : import.meta.glob est une fonctionnalité Vite ; pour les projets non Vite, d'autres solutions d'importation dynamique doivent être envisagées.

Étiquettes: vue vite import.meta.glob dynamic-components component-loading

Publié le 8 juin à 16h30