【问题标题】:How do you use routes from a headless CMS in Nuxt JS (SSR)?您如何在 Nuxt JS (SSR) 中使用来自无头 CMS 的路由?
【发布时间】:2020-10-01 00:43:59
【问题描述】:

问题

我正在尝试使用 CMS 定义的路由来获取内容,该内容用于确定在我的 Nuxt 前端中加载哪个页面。那么,您如何实现自己的逻辑,将路由连接到内容,将内容连接到页面,同时保持 Nuxt 页面的所有出色功能?

任何帮助将不胜感激!

背景

无头 CMS 正在兴起,我们希望在通用模式 (SSR) 下将我们的与 Nuxt 一起使用。

我们坚持使用 Umbraco(一种免费托管的 CMS),它有一个灵活的路由系统,如果没有大量用户的强烈反对,我们无法对其进行限制。

注意:Umbraco 不是无头的,我们自己通过 Umbraco Headrest project on GitHub 添加该功能

注意:每条内容都有一个包含其内容类型名称的字段

例如,下面的内容结构是有效的。

.
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。 Umbraco 将为/other-special-offer/new-special-offer 返回new-special-offer

尝试的解决方案

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

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

1) 中间件方法

我们尝试创建对 CMS API 的异步调用,然后使用响应来确定要加载哪个页面。

middleware.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 上将失败的异步问题作为 this bug 提出
  • 我在this GitHub issuecommented,这是针对同一问题的功能请求,但围绕该问题进行了一些很好的对话

【问题讨论】:

  • 这是延迟响应,但结帐棱镜(无头 CMS),您可以使用它们的链接解析器或路由解析器定义路由,并且可以在 i18n 中使用它。

标签: url-routing umbraco nuxt.js server-side-rendering headless-cms


【解决方案1】:

您可以使用 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 中构建触发器(不确定这是否可能)。

【讨论】:

    【解决方案2】:

    我们尝试的是使用一个通用的页面模板来处理所有可能的路线,代码可以在: 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 钩子是最好的地方。我不确定哪个是最好的,我们正在使用异步数据,这可能不是最佳实践。

    【讨论】:

    • 感谢您的回复和示例,非常感谢!看起来您的解决方案是标准的动态内容方法。虽然这是完全有效的,但它并不是我们想要的。最后,我们选择让我们的 CMS 完全无头,并取消所有路由。为我们提供纯天然的 Nuxt 体验。
    猜你喜欢
    • 2019-08-06
    • 2021-03-29
    • 2020-04-18
    • 2019-11-14
    • 2021-05-22
    • 2022-01-01
    • 2021-11-23
    • 2018-04-23
    • 2023-03-13
    相关资源
    最近更新 更多