Nuxt动态路由适配:基于CMS slug与页面类型映射实现路由
我之前也遇到过类似的CMS自定义slug和Nuxt路由匹配的问题,给你分享几个亲测可行的实现思路:
实现思路与步骤
1. 创建统一动态路由入口
首先在pages目录下创建[slug].vue文件,作为所有自定义slug页面的统一入口——它会匹配所有/xxx形式的URL(注意:Nuxt路由优先级是固定路由>动态路由,建议删掉原来的portfolio、contact固定页面,统一用动态路由处理)。
2. 根据页面类型动态渲染组件
在[slug].vue中,通过异步方法从Wagtail API获取当前slug对应的页面类型,再根据类型动态导入并渲染对应的组件。
Nuxt 2 示例代码:
<template> <div> <component :is="pageComponent" v-if="pageComponent" /> <div v-else>404 页面不存在</div> </div> </template> <script> export default { async asyncData({ params, error }) { // 从Wagtail API获取页面信息(包含pageType) const pageData = await fetch(`你的WagtailAPI地址/pages?slug=${params.slug}`) .then(res => res.json()) .catch(err => { error({ statusCode: 500, message: '页面数据加载失败' }) return null }) if (!pageData) { error({ statusCode: 404, message: '页面不存在' }) return {} } // 根据页面类型匹配对应组件 let pageComponent switch (pageData.pageType) { case 'portfolio.PortfolioIndexPage': pageComponent = () => import('~/pages/portfolio/index.vue') // 原portfolio页面内容迁移到这里 break case 'contact.ContactPage': pageComponent = () => import('~/pages/contact/index.vue') // 原contact页面内容迁移到这里 break default: error({ statusCode: 404, message: '未知页面类型' }) } return { pageComponent, pageData } } } </script>
Nuxt 3 示例代码:
<template> <div> <component :is="pageComponent" v-if="pageComponent" /> <div v-else>404 页面不存在</div> </div> </template> <script setup> const route = useRoute() const { data: pageData, error: fetchError } = await useAsyncData('pageData', () => { return $fetch(`你的WagtailAPI地址/pages?slug=${route.params.slug}`) }) let pageComponent = null if (pageData.value) { switch (pageData.value.pageType) { case 'portfolio.PortfolioIndexPage': pageComponent = await import('~/pages/portfolio/index.vue') break case 'contact.ContactPage': pageComponent = await import('~/pages/contact/index.vue') break default: throw createError({ statusCode: 404, message: '未知页面类型' }) } } if (fetchError.value) { throw createError({ statusCode: 500, message: '页面数据加载失败' }) } </script>
3. 优化nuxt-link跳转逻辑
你提到的理想写法需要调整,Nuxt路由名称基于文件结构,动态路由[slug].vue的默认名称是[slug](Nuxt2)或slug(Nuxt3)。建议传递slug参数的同时带上pageType,避免重复请求API:
Nuxt 2 跳转示例:
<nuxt-link :to="{ name: '[slug]', params: { slug: item.slug, pageType: item.pageType } }" > {{ item.title }} </nuxt-link>
然后在[slug].vue的asyncData里优先使用传递的pageType:
async asyncData({ params, error }) { let pageData, pageType // 如果从nuxt-link传入了pageType,直接复用 if (params.pageType) { pageType = params.pageType pageData = await fetch(`你的WagtailAPI地址/pages?slug=${params.slug}`).then(res => res.json()) } else { // 否则从API获取 pageData = await fetch(`你的WagtailAPI地址/pages?slug=${params.slug}`).then(res => res.json()) pageType = pageData.pageType } // ...后续组件匹配逻辑不变 }
Nuxt 3 跳转示例:
<NuxtLink :to="{ name: 'slug', params: { slug: item.slug, pageType: item.pageType } }" > {{ item.title }} </NuxtLink>
4. 进阶优化:路由中间件统一处理
如果页面类型较多,可以用路由中间件统一处理页面类型的获取和验证,减少重复代码。比如创建middleware/pageType.js:
Nuxt 2 中间件示例:
export default async function({ params, error, store }) { if (!params.pageType) { const pageData = await fetch(`你的WagtailAPI地址/pages?slug=${params.slug}`).then(res => res.json()) if (!pageData) { error({ statusCode: 404, message: '页面不存在' }) return } params.pageType = pageData.pageType store.commit('setPageData', pageData) } }
然后在[slug].vue中启用该中间件:
export default { middleware: 'pageType' }
5. SEO 适配
别忘了根据页面类型和数据设置对应的SEO标签:
Nuxt 2:
head() { return { title: this.pageData?.title || '默认标题', meta: [ { hid: 'description', name: 'description', content: this.pageData?.description || '默认描述' } ] } }
Nuxt 3:
useHead({ title: pageData.value?.title || '默认标题', meta: [ { name: 'description', content: pageData.value?.description || '默认描述' } ] })
这样就能完美实现:URL显示自定义slug,根据CMS返回的页面类型渲染对应组件,同时支持高效的nuxt-link跳转。
内容的提问来源于stack exchange,提问作者Enquest
相关产品推荐
相关产品推荐

