【问题标题】:Ember.js: parallel loading parent and child modelsEmber.js:并行加载父子模型
【发布时间】:2019-09-27 06:35:48
【问题描述】:

我有这些路线:

  • 帖子
    • 索引(所有帖子)
    • 单个(带有动态参数post_id
      • 索引(单个帖子和它的 cmets 视图)
      • 编辑(编辑帖子)

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

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

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

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

【问题讨论】:

    标签: ember.js ember-data


    【解决方案1】:

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

    最简单的情况是使用 Ember Data 以及遵循 JSON:API specification 并支持 inclusion of related resources 的 REST API:

    import Route from '@ember/routing/route';
    
    export default Route.extend({
      model({ post_id }) {
        return this.store.findRecord('post', post_id, { include: 'comments' });
      }
    });
    

    如果你使用普通的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 不支持侧加载,则有点棘手,因为您需要使用查询来加载 cmets。这取决于您的适配器配置,但我想它看起来像这样:

    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-父子路由的Hooks按设计顺序执行。子路由的model-Hook 在其父model-Hook 返回的Promise 解决之前不会被触发。话虽如此,仍然有一些技术可以仅加载所需的数据并缓存在不同子路由之间共享的数据。

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

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

    1. post 和 cmets 未并行加载。
    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;
      }
    });
    

    【讨论】:

    • 但如果我在“posts.single”路由中获取“post”和“comment”,它也会为“posts.single.edit”路由加载“comment”,其中它们是不必要的。另一方面,如果“post”将从“posts.single”路由加载,而“comment”从“posts.single.index”加载,它们不会并行加载?发布后只有 cmets?
    • 是的,路由模型钩子不是并行执行的。但是如果子路由发生变化,父模型钩子不会再次执行。所以从posts.single.indexposts.single.edit 的转换不会导致posts.single 模型钩子再次被执行。如果您的用户很可能会在编辑之前或之后访问posts.single.index 路由,那么在posts.single 路由中加载 cmets 将不是问题。如果这不是您的选择,您可能需要在服务中执行获取逻辑,该服务提供客户端缓存。现在是介绍 Ember Data 的好时机。
    • 那么,没有机会并行获取父子模型?
    • 有很多方法可以并行获取不同的资源,但是model父子路由的钩子不会一个接一个地执行。这是设计使然。或者反过来说:如果事情应该并行执行,你不应该在父模型钩子和子模型钩子之间拆分它。
    • 好的,谢谢。虽然你的回答并没有解决我的问题,除了最后一个,他们很有帮助。
    猜你喜欢
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多