Vue 3:正确实现动态布局渲染与组合api



我试图实现一个好方法来生成动态布局组件,根据我的路由元设置改变(如果路由有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,但没有得到完成一个工作结果。你有什么建议吗?

兄弟,计算不会为你工作,因为webpack导入不能计算meta。布局?然后导入。要解决这个问题,为layout创建一个单独的变量。像这样:

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

注:别管语法了。我在用打字稿。

首先,不应该在计算函数中调用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更改了新路由的元数据,并且每次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>

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

最新更新