Ember.js: 并行加载父子模型
Ember.js: parallel loading parent and child models
我有这些路线:
- posts
- index(所有 posts)
- 单个(带动态参数post_id)
- 索引(单个post及其评论视图)
- 编辑(编辑post)
有两个单独的请求,分别是通过 ID 获取 post 和通过 post ID 获取 post 评论。
我想并行加载 post 和 posts.single.index 路由的评论,因为我在路由名称中有一个 post ID,我不必等待 post 加载。
但是 Ember 加载 posts.single 模型,并且仅在 post 之后加载评论。
是否可以并行调用子模型与父模型?
我找到了解决posts.single不加载任何东西,posts.single.index调用两个请求的方法在它自己的模型中。另一方面,我应该在所有 posts.single 子路由中加载 post 模型,例如 posts.single.edit。当应用程序增长时,这可能是个问题。
有几种技术可以在路由的 model
-Hook 中加载多个资源。哪些最适合您的需求,甚至可能在很大程度上取决于您的应用程序。特别是使用的后端 API 的功能,如果您的 Ember 应用程序使用 Ember Data or plain fetch/ajax 请求,就会有很大的不同。
最简单的情况是使用 Ember 数据以及 JSON:API specification and supports 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 数据执行此操作,但您的 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
-Hook 来并行加载资源。 model
- 父子路由的hooks是按照设计顺序执行的。子路由的 model
-Hook 在其父 model
-Hook 返回的 Promise
被解析之前不会被触发。话虽如此,仍然有一些技术可以只加载需要的数据和缓存在不同子路由之间共享的数据。
让我们以问题中的示例为例,并在对该答案的评论中更详细地说明:我们的前端应该显示一个 post,包括它对一条路线的评论和一个用于编辑相同路线的表格 post在另一条路线上。两条路线都需要相同的 post 资源,但只有一条路线还需要 post 的注释。如果用户从一个路径转换到另一个路径,应用程序不应再次加载 post,如果用户转换到编辑视图,应用程序不应加载评论。
一种天真的尝试是加载 post
资源的父路由和两个子路由,一个用于包含评论的视图,一个用于编辑表单。我称此尝试为 "naive",因为它在三个方面都失败了:
- post 和评论没有并行加载。
- 如果用户通过第三个路由在路由之间转换,则不会缓存资源。
- 它将视觉设计与数据加载相结合。
第三点可能比较混乱。事实上,这是对 Ember 中嵌套路由的常见误解。它们 不是 用于建模数据层次结构,但应该用于在子路径之间共享视觉 UI。重要的不是 model
-Hook,而是父 {{outlet}}
.
中子模板的呈现
您的所有顾虑都可以通过在客户端缓存资源的服务轻松解决。这是 Ember 数据的主要特征之一。这也是 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
- index(所有 posts)
- 单个(带动态参数post_id)
- 索引(单个post及其评论视图)
- 编辑(编辑post)
有两个单独的请求,分别是通过 ID 获取 post 和通过 post ID 获取 post 评论。 我想并行加载 post 和 posts.single.index 路由的评论,因为我在路由名称中有一个 post ID,我不必等待 post 加载。
但是 Ember 加载 posts.single 模型,并且仅在 post 之后加载评论。
是否可以并行调用子模型与父模型?
我找到了解决posts.single不加载任何东西,posts.single.index调用两个请求的方法在它自己的模型中。另一方面,我应该在所有 posts.single 子路由中加载 post 模型,例如 posts.single.edit。当应用程序增长时,这可能是个问题。
有几种技术可以在路由的 model
-Hook 中加载多个资源。哪些最适合您的需求,甚至可能在很大程度上取决于您的应用程序。特别是使用的后端 API 的功能,如果您的 Ember 应用程序使用 Ember Data or plain fetch/ajax 请求,就会有很大的不同。
最简单的情况是使用 Ember 数据以及 JSON:API specification and supports inclusion of related resources:
之后的 REST APIimport 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 数据执行此操作,但您的 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
-Hook 来并行加载资源。 model
- 父子路由的hooks是按照设计顺序执行的。子路由的 model
-Hook 在其父 model
-Hook 返回的 Promise
被解析之前不会被触发。话虽如此,仍然有一些技术可以只加载需要的数据和缓存在不同子路由之间共享的数据。
让我们以问题中的示例为例,并在对该答案的评论中更详细地说明:我们的前端应该显示一个 post,包括它对一条路线的评论和一个用于编辑相同路线的表格 post在另一条路线上。两条路线都需要相同的 post 资源,但只有一条路线还需要 post 的注释。如果用户从一个路径转换到另一个路径,应用程序不应再次加载 post,如果用户转换到编辑视图,应用程序不应加载评论。
一种天真的尝试是加载 post
资源的父路由和两个子路由,一个用于包含评论的视图,一个用于编辑表单。我称此尝试为 "naive",因为它在三个方面都失败了:
- post 和评论没有并行加载。
- 如果用户通过第三个路由在路由之间转换,则不会缓存资源。
- 它将视觉设计与数据加载相结合。
第三点可能比较混乱。事实上,这是对 Ember 中嵌套路由的常见误解。它们 不是 用于建模数据层次结构,但应该用于在子路径之间共享视觉 UI。重要的不是 model
-Hook,而是父 {{outlet}}
.
您的所有顾虑都可以通过在客户端缓存资源的服务轻松解决。这是 Ember 数据的主要特征之一。这也是 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;
}
});