基于 URL 参数动态导入 Vue 组件

Dynamically Import Vue Component Based On URL Params

我正在尝试根据 url 参数中输入的路径导入 vue 组件。因此,例如,如果在浏览器中输入 <host>/<path>,我想导入一个位于 <path>.vue.

的 vue 组件

在我的 routes.js 文件中,我有一个将获得嵌套路径的路由:

  { path: 'object/:catchAll(.*)*', component: BaseObject }

并发送到BaseObject:

<template>
  <div>
    <component :is="componentFile" />
  </div>
</template>
<script>
import { mapState, mapActions } from 'vuex'

export default {
  name: 'BaseObject',
  data () {
    return {
      componentPath: '',
      address: ''
    }
  },
  methods: {
    importComponent (path) {
      return () => import(`./${path}.vue`)
    }
  },
  computed: {
    componentFile () {
      return this.importComponent(this.componentPath)
    }
  },
  created () {
    const params = this.$route.params.catchAll
    console.log(params)
    this.address = params.pop()
    this.componentPath = params.join('/')
  }
}
</script>

当我导航到 http://localhost:8080/#/object/action 时,我希望加载位于 ./action.vue 的组件。但这并没有发生 - 相反我收到以下错误:

runtime-core.esm-bundler.js?9e79:38 [Vue warn]: Invalid VNode type: undefined (undefined) 
  at <Anonymous> 
  at <BaseObject onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< null > > 
  at <RouterView> 
  at <QPageContainer> 
  at <QLayout view="lHh Lpr lFf" > 
  at <MainLayout onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< Proxy {$i18n: {…}, $t: ƒ, …} > > 
  at <RouterView> 
  at <App>

Uncaught (in promise) Error: Cannot find module './.vue'
        at app.js:416

有人知道如何实现吗?

我猜你正在使用 Webpack 来捆绑你的应用程序并解析 JS 模块。正如您在文档 https://webpack.js.org/api/module-methods/#import-1import() returns 中看到的承诺,它是异步工​​作的。在使用该组件之前,您必须先解决该承诺。

methods: {
  async getComponentFile() {
    const component = await this.importComponent(this.componentPath);
    return component;
  }
},

不难易解!

<component :is="componentFile" />
export default {
 name: 'BaseObject',

 components: {
  firstComponent: () => import('...'),
  secondComponent: () => import('...'),
  thirdComponent: () => import('...')
 }

 computed: {
  componentFile () {
   return this.detectComponentBasedPath()
  }
 },

 methods: {
  detectComponentBasedPath () {
   ...
  }
 }
}
</script>

您的代码至少有 2 个问题...

  1. 你的“catch all”路线被定义为{ path: 'object/:catchAll(.*)*', component: BaseObject }所以如果你导航到URLhttp://localhost:8080/#/object/action“对象”部分匹配并且catchAll param 将是一个包含单个项目“action”的数组。所以 created 钩子将弹出这个单个项目,params 数组保持为空并且 componentPath 也将为空(这是 Cannot find module './.vue' 错误的原因)

  2. 在 Vue 3 中,旧的异步组件语法 (() => import(``./${path}.vue``)) is deprecated。创建异步组件时,您应该始终使用 defineAsyncComponent 帮助程序(这是 Invalid VNode type: undefined Vue 警告的原因)

所以你 BaseObject 应该是这样的:

<template>
  <div>
    <component :is="componentFile" />
  </div>
</template>
<script>
import { defineComponent, defineAsyncComponent } from "vue";

export default defineComponent({
  name: "BaseObject",
  data() {
    return {
      componentPath: "",
      address: "",
    };
  },
  methods: {},
  computed: {
    componentFile() {
      return defineAsyncComponent(() =>
        import(`../components/${this.componentPath}.vue`)
      );
    },
  },
  created() {
    const params = this.$route.params.catchAll;
    console.log(this.$route.params);
    // this.address = params.pop();
    this.componentPath = params.join("/");
  },
});
</script>

Working demo

另请注意,像这样定义“捕获所有”路由是危险的,因为它将匹配所有路由 - /object/action/object/action/dd/object/action/dd/bb 等,而这些组件不会存在。所以也许只允许一层嵌套会更好...