Vue 3:使用合成API正确实现动态布局渲染

rjee0c15  于 2023-03-13  发布在  Vue.js
关注(0)|答案(2)|浏览(123)

我正在尝试实现一种生成动态布局组件的好方法,这些组件根据我的路由 meta设置而变化(如果路由有meta: { layout: 'LayoutName' },它应该加载该布局,否则它加载默认的MainLayout.vue)。
经过大量的尝试和错误我设法让它工作

// My main App.vue file
<template>
    <component :is="layout">
        <router-view />
    </component>
</template>

<script setup>

import { defineAsyncComponent, computed } from 'vue'
import { useRoute } from 'vue-router';

let layout = computed( () => { 
        const { meta } = useRoute()
        return defineAsyncComponent( () => import(`./layouts/${meta.layout ?? 'MainLayout'}.vue`) )
    }
)

我的问题是:这种混合computeddefineAsyncComponent的方式是否可取?
我尝试使用watchwatchEffect,但没有达到工作效果。您有什么建议吗?

lc8prwob

lc8prwob1#

兄弟,计算不会为你工作,因为webpack导入不能计算 meta布局??并进行导入。要解决这个问题,使一个单独的变量布局。类似的东西:

layout(): ILayout {
  const { meta } = useRoute();
  const layoutName: TLayout = meta.layout ?? 'DefaultLayout';
  return defineAsyncComponent(
    () =>
      import(/* webpackChunkName: 'Layout' */ `@/layouts/${layoutName}.vue`)
  );
}

别在意语法,我用的是 typescript 。

csbfibhn

csbfibhn2#

首先,你不应该在一个计算函数中调用useRoute。你应该在设置层调用它。所以你的代码应该是:

<script lang="ts">
import { computed, defineAsyncComponent, defineComponent } from "vue"
import { useRoute } from "vue-router"

export default defineComponent({
  setup() {
    const route = useRoute()
    const layout_component = computed(() => {
      const layout = route.meta.layout ?? "default"
      return defineAsyncComponent(() => import(`@/layouts/${layout}.vue`))
    })
    return { layout_component }
  },
})
</script>

<template>
  <component :is="layout_component">
    <router-view />
  </component>
</template>

现在,这种方法是可行的,但它会导致<component>在每次页面更改时重新呈现,这是因为route.meta更改为新路由的 meta,并且defineAsyncComponent每次都返回一个新组件(实际上,即使您以某种方式缓存它,<component>仍然会重新呈现,因为它检测到它的依赖关系已经被重新计算-即使是相同的值)。
使用显式watch而不是computed效果更好:

<script lang="ts">
import {
  Component,
  defineAsyncComponent,
  defineComponent,
  shallowRef,
  watch,
} from "vue"
import { useRoute } from "vue-router"

export default defineComponent({
  setup() {
    const route = useRoute()
    const layout_component = shallowRef<Component>()
    watch(
      () => route.meta.layout ?? "default",
      layout => {
        layout_component.value = defineAsyncComponent(() =>
          import(`@/layouts/${layout}.vue`),
        )
      },
      { immediate: true },
    )
    return { layout_component }
  },
})
</script>

<template>
  <component :is="layout_component">
    <router-view />
  </component>
</template>

这样,如果布局没有更改,则在页面更改时不会重新呈现布局组件。

相关问题