问题
我正在尝试使用 CMS 定义的路由来获取内容,该内容用于确定在我的 Nuxt 前端中加载哪个页面。那么,您如何实现自己的逻辑来将路由连接到内容,并将内容连接到页面,同时保持 Nuxt 页面的所有出色功能?
任何帮助将不胜感激!
上下文
无头 CMS 正在兴起,我们希望在通用模式 (SSR) 下将我们的与 Nuxt 一起使用。
我们使用 Umbraco(一种免费托管的 CMS),它有一个灵活的路由系统,如果没有大量用户的强烈反对,我们就无法限制它。
注意:Umbraco 不是无头的,我们自己在 GitHub 上的 Umbraco Headrest 项目中添加了该功能
注意:每条内容都有一个包含其内容类型名称的字段
例如,以下内容结构是有效的。
.
home
├── events
| ├── event-one
| └── event-two
| ├── event-special-offer
| └── some-other-content
├── special-offer
└── other-special-offer
所以,如果我们想用 nuxt 渲染这些特价商品,我们需要使用 Pages/SpecialOffer.vue。
问题
问题是这些特价商品的默认路径是:
/special-offer
/other-special-offer
/events/event-two/event-special-offer
编辑器还可以创建自定义路径,例如:
/holiday2020/special
(可能指向event-special-offer
)
编辑器还可以重命名内容,因此other-special-offer
可以成为new-special-offer
. new-special-offer
然后,Umbraco 将返回/other-special-offer
和/new-special-offer
。
尝试的解决方案
以下方法很有希望,但要么没有奏效,要么只是部分成功。
- 使用中间件将 URL 请求转发到 Umbraco 并根据结果确定要加载的组件(失败)
- 使用插件将 URL 请求转发到 Umbraco 并根据结果确定要加载的组件(失败)
- 从内容映射路线(部分)
- 使用使用响应数据加载动态组件的单个页面(部分)
1) 中间件方式
我们尝试创建对 CMS API 的异步调用,然后使用响应来确定要加载哪个页面。
中间件.js
export default function (context) {
return new Promise((resolve, reject) => {
/**
* Prepend the cms provided url's path with /content,
* which the API knows is for CMS content
*/
context.app.$axios.$get(`/content${context.route.path}`)
.then((content) => {
// Save the resulting content to vuex
context.store.commit('umbraco/load', content)
const { routes } = context.app.router.options
/**
* The content has a contentType property which is a string
* containing the name of the component we want to load
*
* This finds the corresponding route
*/
const wantedRoute = routes.find(x => x.name === content.contentType)
// Sets the component to load as /Pages/[componentName].vue
context.route.matched[0].components = {
default: wantedRoute.component
}
resolve()
})
})
问题
在服务器上运行时,中间件似乎在路由解析后运行,导致如下错误:
vue.runtime.esm.js?2b0e:619 [Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render.
注意:第一次访问时,中间件只运行在服务器端,后续访问时,中间件只运行在客户端。
2) 插件方法
由于插件在生命周期的早期运行,我们认为我们会在那里尝试相同的方法并尝试调用 CMS 的 API 并根据结果确定要加载的组件。
插件.js
await context.app.router.beforeEach( async ( to, from, next ) => {
/**
* Prepend the cms provided url's path with /content,
* which the API knows is for CMS content
*/
const content = await context.app.$axios.$get( `/content${context.route.path}` );
// Save the resulting content to vuex
context.store.commit('cms/load', content);
/**
* The content has a contentType property which is a string
* containing the name of the component we want to load
*
* This finds the corresponding route
*/
const routes = context.app.router.options.routes;
const targetRoute = routes.find( r => r.name === content.contentType );
// Sets the component to load as /Pages/[componentName].vue
to.matched[0].components = {
default: targetRoute.component
};
next();
})
注意:插件在第一页访问时同时在服务器和客户端上运行。在随后的访问中,它们仅在客户端上运行。
问题
不幸的是,这也不起作用,因为app.router.beforeEach()
不允许异步代码。因此,API 请求从未得到解决。
3) 路线图法
除非您在构建后更改路线,否则生成路线图列表工作正常。这里的问题是您无法在运行时更新路由,因此在 CMS 中重命名或添加新内容不会反映在 Nuxt 中。
4) 动态组件方法
这种方法似乎是我们问题的默认解决方案,它涉及在 Nuxt 中使用单个页面来调用 API,然后动态加载组件。不幸的是,这种方法删除了 Nuxt 的大部分页面特定功能(例如页面转换)。
失去这些功能真的很不幸,因为 Nuxt 非常好,我们希望尽可能多地使用它!
Nuxt JS GitHub 问题
- 我在 nuxt.js GitHub 上提出了失败的异步问题作为这个错误
- 我评论了这个 GitHub 问题,这是针对同一问题的功能请求,但围绕该问题进行了一些很好的对话