10

问题

我正在尝试使用 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

尝试的解决方案

以下方法很有希望,但要么没有奏效,要么只是部分成功。

  1. 使用中间件将 URL 请求转发到 Umbraco 并根据结果确定要加载的组件(失败
  2. 使用插件将 URL 请求转发到 Umbraco 并根据结果确定要加载的组件(失败
  3. 从内容映射路线(部分
  4. 使用使用响应数据加载动态组件的单个页面(部分

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 问题,这是针对同一问题的功能请求,但围绕该问题进行了一些很好的对话
4

2 回答 2

1

我们尝试的是使用一个通用页面模板来处理所有可能的路线,代码可以在: https ://github.com/dpc-sdp/ripple/blob/v1.23.1/packages/ripple-nuxt-tide/ lib/module.js#L208

该单页模板将匹配*用户要求的路线。但是,您可以根据需要为多个路由规则定义多个模板。

Nuxt 应用程序和 CMS API 之间的通信如下所示:

Nuxt嗨 CMS,用户想要路径的数据/about-us

CMS知道了。让我检查 CMS 数据库,并找到相同的 url。如果有,我将数据发回给你,否则我会给你一个404

这样您就不会丢失页面转换功能。在该 Nuxt 页面模板中,您可以向 CMS 发送请求并获取页面数据。这可以在 Nuxt 路由器中间件或页面异步数据中完成,现在也许fetch钩子是最好的地方。我不确定哪个是最好的,我们正在使用异步数据,这可能不是最佳实践。

于 2021-08-07T08:48:42.273 回答
0

您可以使用 extendRoutes 在站点部署中构建路由。

nuxt.config.js

// Router middleware
router: {
    async extendRoutes (routes, resolve) {
        // pulls in routes from external file
        await customRoutes(routes, resolve)
    }
},

然后是这样的:

// extendRoutes snippet
let pageRoutes = require('./routes-bkp.json')

try {
  const { data: { pages } } = await getPagesForRoutes.queryCMS()
  pageRoutes = pages
  console.log('Route Request Succeeded.')
} catch {
  console.log('Route Request Succeeded.!  Using backup version.')
}

pageRoutes.forEach(({ slug }) => {
  routes.unshift({
    name: slug,
    path: `/:page(${slug})`,
    component: '~/pages/_page'
  })
})

在你的功能中。然后,您将需要 Umbraco 中的构建触发器(不确定这是否可能)。

于 2021-12-22T11:29:19.523 回答