首页 > 解决方案 > Ember.js:并行加载父子模型

问题描述

我有这些路线:

按 ID 获取帖子和按帖子 ID 获取帖子评论有两个单独的请求。我想并行加载posts.single.index路由的帖子和评论,因为我在路由名称中有一个帖子ID,并且我不必等待何时加载帖子。

但是 Ember 会加载posts.single模型,并且只有在发布后才会加载评论。

是否可以与父模型并行调用子模型?

当posts.single不加载任何东西并且posts.single.index在它自己的模型中调用两个请求时,我找到了一个解决方案。另一方面,我应该在所有posts.single 子路由中加载 post 模型,例如posts.single.edit。当应用程序增长时,这可能是一个问题。

标签: ember.jsember-data

解决方案


有几种技术可以在model路由的 -Hook 中加载多个资源。哪些最适合您的需求,甚至可能在很大程度上取决于您的应用。尤其是使用的后端 API 的功能以及您的 Ember 应用程序是否使用Ember Data或普通的fetch /ajax 请求会产生很大的不同。

最简单的情况是将 Ember Data 与遵循JSON:API 规范并支持包含相关资源的 REST API 一起使用:

import Route from '@ember/routing/route';

export default Route.extend({
  model({ post_id }) {
    return this.store.findRecord('post', post_id, { include: 'comments' });
  }
});

如果您使用的是 plain fetch,您可以使用Promise.all()并行加载多条记录:

import Route from '@ember/routing/route';

export default Route.extend({
  async model({ post_id }) {
    let [post, comments] = await Promise.all([
      fetch(`/posts/${post_id}`),
      fetch(`/posts/${post_id}/comments`),
    ]);
    return { post, comments };
  }
});

如果你不喜欢Promise.all()数组破坏的语法,你可能想看看RSVP.hash(). rsvp默认情况下与 ember 捆绑在一起。

如果使用 Ember Data 执行此操作,但您的 API 不支持侧加载,则有点棘手,因为您需要使用查询来加载评论。这取决于您的适配器配置,但我想它看起来像这样:

import Route from '@ember/routing/route';

export default Route.extend({
  async model({ post_id }) {
    let [post, comments] = await Promise.all([
      this.store.findRecord('post', post_id),
      this.store.query('comment', {
        filter: {
          post: post_id
        }
      })
    ]);
    return { post, comments };
  }
});

您不能使用多个model-Hooks 并行加载资源。model- 父子路由的钩子按设计顺序执行。子路由的-Hook在其父-Hook返回model之前不会被触发。话虽如此,仍然有一些技术可以仅加载所需的数据并缓存在不同子路由之间共享的数据。Promisemodel

让我们以问题中的示例为例,并在对此答案的评论中进行更详细的说明:我们的前端应该显示一个帖子,其中包括对一条路线的评论和一个用于在另一条路线上编辑同一帖子的表单。两条路线都需要相同的帖子资源,但只有一条还需要帖子的评论。如果用户从一条路线转换到另一条路线,则应用程序不应再次加载帖子,如果用户转换到编辑视图,则不应加载评论。

一个天真的尝试是加载post资源的父路由和两个子路由,一个用于包含评论的视图,一个用于编辑表单。我将这种尝试称为“幼稚”,因为它以三种方式失败:

  1. 帖子和评论不是并行加载的。
  2. 如果用户通过第三个路由在路由之间转换,则不会缓存资源。
  3. 它将视觉设计与数据加载相结合。

第三点可能令人困惑。事实上,这是对 Ember 中嵌套路由的常见误解。它们不是用来建模数据层次结构,而是应该用于在子路由之间共享可视 UI。重要的不是model-Hook,而是父模板中子模板的呈现{{outlet}}

您的所有问题都可以通过在客户端缓存资源的服务轻松解决。这是 Ember Data 的主要功能之一。这也是 GraphQL 的 Apollo 客户端最受宣传的功能之一。就这么简单:大多数复杂的前端应用程序都需要一个客户端缓存层来防止资源过度获取。如果您面临该要求,我建议您使用现有解决方案之一。对于简单的用例,您还可以构建自己的服务。一个基本的实现可能如下所示:

import Service from '@ember/service';

export default class StoreService extend Sevice({
  postCache = new Map();

  loadPost(id) {
    let { postCache } = this;
    if (postCache.has(id)) {
      return postCache.get(id);
    }

    let request = fetch(`/posts/${id}`);
    postCache.set(id, request);
    return request;
  }
});

推荐阅读